import { describe, expect, it } from 'vitest';

import { Notification } from '@core/notifications/domain/entities/Notification';

import { NotificationOrmEntity } from '../entities/NotificationOrmEntity';
import { TypeOrmPersistenceSchemaError } from '../errors/TypeOrmPersistenceSchemaError';
import { NotificationOrmMapper } from './NotificationOrmMapper';

describe('NotificationOrmMapper', () => {
  it('toDomain preserves persisted identity and uses reconstitute semantics', () => {
    const mapper = new NotificationOrmMapper();

    const entity = new NotificationOrmEntity();
    entity.id = '00000000-0000-4000-8000-000000000001';
    entity.recipientId = 'driver-123';
    entity.type = 'race_reminder';
    entity.title = 'Race Starting Soon';
    entity.body = 'Your race starts in 15 minutes';
    entity.channel = 'in_app';
    entity.status = 'unread';
    entity.urgency = 'silent';
    entity.data = { raceId: 'race-456' };
    entity.actionUrl = null;
    entity.actions = null;
    entity.requiresResponse = false;
    entity.createdAt = new Date('2025-01-01T00:00:00.000Z');
    entity.readAt = null;
    entity.respondedAt = null;
    entity.updatedAt = new Date('2025-01-01T00:00:00.000Z');

    const domain = mapper.toDomain(entity);

    expect(domain.id).toBe(entity.id);
    expect(domain.recipientId).toBe(entity.recipientId);
    expect(domain.type).toBe(entity.type);
    expect(domain.title).toBe(entity.title);
    expect(domain.body).toBe(entity.body);
    expect(domain.channel).toBe(entity.channel);
    expect(domain.status).toBe(entity.status);
    expect(domain.urgency).toBe(entity.urgency);
    expect(domain.data).toEqual({ raceId: 'race-456' });
    expect(domain.createdAt).toEqual(entity.createdAt);
    expect(domain.readAt).toBeUndefined();
    expect(domain.respondedAt).toBeUndefined();
  });

  it('toDomain validates persisted shape and throws adapter-scoped schema error', () => {
    const mapper = new NotificationOrmMapper();

    const entity = new NotificationOrmEntity();
    entity.id = '00000000-0000-4000-8000-000000000001';
    entity.recipientId = 123 as unknown as string; // Invalid
    entity.type = 'race_reminder';
    entity.title = 'Race Starting Soon';
    entity.body = 'Your race starts in 15 minutes';
    entity.channel = 'in_app';
    entity.status = 'unread';
    entity.urgency = 'silent';
    entity.data = null;
    entity.actionUrl = null;
    entity.actions = null;
    entity.requiresResponse = false;
    entity.createdAt = new Date('2025-01-01T00:00:00.000Z');
    entity.readAt = null;
    entity.respondedAt = null;
    entity.updatedAt = new Date('2025-01-01T00:00:00.000Z');

    try {
      mapper.toDomain(entity);
      throw new Error('expected-to-throw');
    } catch (error) {
      expect(error).toBeInstanceOf(TypeOrmPersistenceSchemaError);
      expect(error).toMatchObject({
        entityName: 'Notification',
        fieldName: 'recipientId',
        reason: 'invalid_string',
      });
    }
  });

  it('toOrmEntity converts domain entity to ORM entity', () => {
    const mapper = new NotificationOrmMapper();

    const domain = Notification.create({
      id: '00000000-0000-4000-8000-000000000001',
      recipientId: 'driver-123',
      type: 'race_reminder',
      title: 'Race Starting Soon',
      body: 'Your race starts in 15 minutes',
      channel: 'in_app',
      data: { raceId: 'race-456' },
      urgency: 'silent',
    });

    const entity = mapper.toOrmEntity(domain);

    expect(entity.id).toBe(domain.id);
    expect(entity.recipientId).toBe(domain.recipientId);
    expect(entity.type).toBe(domain.type);
    expect(entity.title).toBe(domain.title);
    expect(entity.body).toBe(domain.body);
    expect(entity.channel).toBe(domain.channel);
    expect(entity.status).toBe(domain.status);
    expect(entity.urgency).toBe(domain.urgency);
    expect(entity.data).toEqual({ raceId: 'race-456' });
    expect(entity.requiresResponse).toBe(false);
    expect(entity.createdAt).toEqual(domain.createdAt);
  });

  it('toDomain handles optional fields correctly', () => {
    const mapper = new NotificationOrmMapper();

    const entity = new NotificationOrmEntity();
    entity.id = '00000000-0000-4000-8000-000000000001';
    entity.recipientId = 'driver-123';
    entity.type = 'protest_filed';
    entity.title = 'Protest Filed';
    entity.body = 'A protest has been filed against you';
    entity.channel = 'email';
    entity.status = 'action_required';
    entity.urgency = 'modal';
    entity.data = { protestId: 'protest-789', deadline: new Date('2025-01-02T00:00:00.000Z') };
    entity.actionUrl = '/protests/protest-789';
    entity.actions = [
      { label: 'Submit Defense', type: 'primary', href: '/protests/protest-789/defense' },
      { label: 'Dismiss', type: 'secondary', actionId: 'dismiss' },
    ];
    entity.requiresResponse = true;
    entity.createdAt = new Date('2025-01-01T00:00:00.000Z');
    entity.readAt = new Date('2025-01-01T01:00:00.000Z');
    entity.respondedAt = null;
    entity.updatedAt = new Date('2025-01-01T01:00:00.000Z');

    const domain = mapper.toDomain(entity);

    expect(domain.actionUrl).toBe('/protests/protest-789');
    expect(domain.actions).toHaveLength(2);
    expect(domain.requiresResponse).toBe(true);
    expect(domain.readAt).toEqual(new Date('2025-01-01T01:00:00.000Z'));
    expect(domain.respondedAt).toBeUndefined();
  });

  it('toDomain handles action_required status with deadline', () => {
    const mapper = new NotificationOrmMapper();

    const entity = new NotificationOrmEntity();
    entity.id = '00000000-0000-4000-8000-000000000001';
    entity.recipientId = 'driver-123';
    entity.type = 'protest_filed';
    entity.title = 'Protest Filed';
    entity.body = 'A protest has been filed against you';
    entity.channel = 'in_app';
    entity.status = 'action_required';
    entity.urgency = 'modal';
    entity.data = { protestId: 'protest-789', deadline: '2025-01-02T00:00:00.000Z' };
    entity.actionUrl = null;
    entity.actions = [{ label: 'Submit Defense', type: 'primary', href: '/defense' }];
    entity.requiresResponse = true;
    entity.createdAt = new Date('2025-01-01T00:00:00.000Z');
    entity.readAt = null;
    entity.respondedAt = null;
    entity.updatedAt = new Date('2025-01-01T00:00:00.000Z');

    const domain = mapper.toDomain(entity);

    expect(domain.status).toBe('action_required');
    expect(domain.requiresResponse).toBe(true);
    expect(domain.urgency).toBe('modal');
    expect(domain.data?.deadline).toBeInstanceOf(Date);
  });
});