import { type IListingRepository } from '@modules/listings/domain/repositories/listing.repository';
import { BulkModerateListingsCommand } from '../commands/bulk-moderate-listings/bulk-moderate-listings.command';
import { BulkModerateListingsHandler } from '../commands/bulk-moderate-listings/bulk-moderate-listings.handler';

function createMockListing(id: string, status = 'PENDING_REVIEW') {
  return {
    id,
    status,
    approve: vi.fn(),
    reject: vi.fn(),
    setModerationScore: vi.fn(),
  };
}

describe('BulkModerateListingsHandler', () => {
  let handler: BulkModerateListingsHandler;
  let mockListingRepo: { [K in keyof IListingRepository]: ReturnType<typeof vi.fn> };
  let mockEventBus: { publish: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockListingRepo = {
      findById: vi.fn(),
      findByIdWithProperty: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
      search: vi.fn(),
      findByStatus: vi.fn(),
      findBySellerId: vi.fn(),
    };
    mockEventBus = { publish: vi.fn() };
    handler = new BulkModerateListingsHandler(mockListingRepo as any, mockEventBus as any);
  });

  it('approves multiple listings', async () => {
    const listing1 = createMockListing('l1');
    const listing2 = createMockListing('l2');
    mockListingRepo.findById
      .mockResolvedValueOnce(listing1)
      .mockResolvedValueOnce(listing2);
    mockListingRepo.update.mockResolvedValue(undefined);

    const command = new BulkModerateListingsCommand(['l1', 'l2'], 'admin-1', 'approve');
    const result = await handler.execute(command);

    expect(result.succeeded).toEqual(['l1', 'l2']);
    expect(result.failed).toHaveLength(0);
    expect(listing1.approve).toHaveBeenCalled();
    expect(listing2.approve).toHaveBeenCalled();
    expect(mockEventBus.publish).toHaveBeenCalledTimes(2);
  });

  it('rejects multiple listings with reason', async () => {
    const listing1 = createMockListing('l1');
    mockListingRepo.findById.mockResolvedValueOnce(listing1);
    mockListingRepo.update.mockResolvedValue(undefined);

    const command = new BulkModerateListingsCommand(['l1'], 'admin-1', 'reject', 'Vi phạm chính sách');
    const result = await handler.execute(command);

    expect(result.succeeded).toEqual(['l1']);
    expect(listing1.reject).toHaveBeenCalledWith('Vi phạm chính sách');
  });

  it('handles mixed success and failure', async () => {
    const listing1 = createMockListing('l1');
    mockListingRepo.findById
      .mockResolvedValueOnce(listing1)
      .mockResolvedValueOnce(null);
    mockListingRepo.update.mockResolvedValue(undefined);

    const command = new BulkModerateListingsCommand(['l1', 'l2'], 'admin-1', 'approve');
    const result = await handler.execute(command);

    expect(result.succeeded).toEqual(['l1']);
    expect(result.failed).toEqual([{ listingId: 'l2', reason: 'Listing không tồn tại' }]);
  });

  it('skips listings not in PENDING_REVIEW status', async () => {
    const listing = createMockListing('l1', 'ACTIVE');
    mockListingRepo.findById.mockResolvedValueOnce(listing);

    const command = new BulkModerateListingsCommand(['l1'], 'admin-1', 'approve');
    const result = await handler.execute(command);

    expect(result.succeeded).toHaveLength(0);
    expect(result.failed).toEqual([{ listingId: 'l1', reason: 'Trạng thái hiện tại: ACTIVE' }]);
  });

  it('throws on empty listing ids', async () => {
    const command = new BulkModerateListingsCommand([], 'admin-1', 'approve');
    await expect(handler.execute(command)).rejects.toThrow('rỗng');
  });

  it('throws when rejecting without reason', async () => {
    const command = new BulkModerateListingsCommand(['l1'], 'admin-1', 'reject');
    await expect(handler.execute(command)).rejects.toThrow('Lý do');
  });
});
