import { CreateSavedSearchCommand } from '../commands/create-saved-search/create-saved-search.command';
import { CreateSavedSearchHandler } from '../commands/create-saved-search/create-saved-search.handler';

describe('CreateSavedSearchHandler', () => {
  let handler: CreateSavedSearchHandler;
  let mockPrisma: any;
  let mockQueryBus: { execute: ReturnType<typeof vi.fn> };
  let mockCommandBus: { execute: ReturnType<typeof vi.fn> };
  let mockLogger: { log: ReturnType<typeof vi.fn>; warn: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockPrisma = {
      savedSearch: {
        create: vi.fn(),
        findMany: vi.fn(),
        count: vi.fn(),
      },
    };
    mockQueryBus = { execute: vi.fn() };
    mockCommandBus = { execute: vi.fn() };
    mockLogger = { log: vi.fn(), warn: vi.fn() };

    handler = new CreateSavedSearchHandler(
      mockPrisma,
      mockQueryBus as any,
      mockCommandBus as any,
      mockLogger as any,
    );
  });

  it('creates a saved search successfully', async () => {
    mockQueryBus.execute.mockResolvedValue({
      metric: 'searches_saved',
      limit: 10,
      used: 2,
      remaining: 8,
      allowed: true,
    });

    const now = new Date();
    mockPrisma.savedSearch.create.mockResolvedValue({
      id: 'saved-1',
      userId: 'user-1',
      name: 'Chung cư Q7',
      filters: { district: 'Quan 7', propertyType: 'APARTMENT' },
      alertEnabled: true,
      lastAlertAt: null,
      createdAt: now,
    });

    mockCommandBus.execute.mockResolvedValue({ usageRecordId: 'usage-1' });

    const command = new CreateSavedSearchCommand(
      'user-1',
      'Chung cư Q7',
      { district: 'Quan 7', propertyType: 'APARTMENT' },
      true,
    );
    const result = await handler.execute(command);

    expect(result.name).toBe('Chung cư Q7');
    expect(result.alertEnabled).toBe(true);
    expect(mockPrisma.savedSearch.create).toHaveBeenCalledTimes(1);
    expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); // Usage metering
  });

  it('throws when name is empty', async () => {
    const command = new CreateSavedSearchCommand('user-1', '', {}, true);
    await expect(handler.execute(command)).rejects.toThrow('Tên tìm kiếm không được để trống');
  });

  it('throws when name exceeds 100 characters', async () => {
    const longName = 'a'.repeat(101);
    const command = new CreateSavedSearchCommand('user-1', longName, {}, true);
    await expect(handler.execute(command)).rejects.toThrow('Tên tìm kiếm không được vượt quá 100 ký tự');
  });

  it('throws when quota is exceeded', async () => {
    mockQueryBus.execute.mockResolvedValue({
      metric: 'searches_saved',
      limit: 5,
      used: 5,
      remaining: 0,
      allowed: false,
    });

    const command = new CreateSavedSearchCommand('user-1', 'Test', {}, true);
    await expect(handler.execute(command)).rejects.toThrow('giới hạn');
  });

  it('continues even when usage metering fails', async () => {
    mockQueryBus.execute.mockResolvedValue({
      metric: 'searches_saved',
      limit: 10,
      used: 2,
      remaining: 8,
      allowed: true,
    });

    const now = new Date();
    mockPrisma.savedSearch.create.mockResolvedValue({
      id: 'saved-1',
      userId: 'user-1',
      name: 'Test',
      filters: {},
      alertEnabled: true,
      lastAlertAt: null,
      createdAt: now,
    });

    mockCommandBus.execute.mockRejectedValue(new Error('Metering failed'));

    const command = new CreateSavedSearchCommand('user-1', 'Test', {}, true);
    const result = await handler.execute(command);

    expect(result.id).toBe('saved-1');
    expect(mockLogger.warn).toHaveBeenCalledWith(
      expect.stringContaining('Usage metering failed'),
      'CreateSavedSearchHandler',
    );
  });
});
