import { ApproveKycCommand } from '../../../application/commands/approve-kyc/approve-kyc.command';
import { ApproveListingCommand } from '../../../application/commands/approve-listing/approve-listing.command';
import { BulkModerateListingsCommand } from '../../../application/commands/bulk-moderate-listings/bulk-moderate-listings.command';
import { RejectKycCommand } from '../../../application/commands/reject-kyc/reject-kyc.command';
import { RejectListingCommand } from '../../../application/commands/reject-listing/reject-listing.command';
import { GetKycQueueQuery } from '../../../application/queries/get-kyc-queue/get-kyc-queue.query';
import { GetModerationQueueQuery } from '../../../application/queries/get-moderation-queue/get-moderation-queue.query';
import { AdminModerationController } from '../admin-moderation.controller';

describe('AdminModerationController', () => {
  let controller: AdminModerationController;
  let mockCommandBus: { execute: ReturnType<typeof vi.fn> };
  let mockQueryBus: { execute: ReturnType<typeof vi.fn> };

  const mockAdmin = { sub: 'admin-1', phone: '0901234567', role: 'ADMIN' };

  beforeEach(() => {
    mockCommandBus = { execute: vi.fn() };
    mockQueryBus = { execute: vi.fn() };
    controller = new AdminModerationController(mockCommandBus as any, mockQueryBus as any);
  });

  describe('GET /admin/moderation — getModerationQueue', () => {
    it('dispatches GetModerationQueueQuery with defaults', async () => {
      const expected = { data: [], total: 0, page: 1, limit: 20, totalPages: 0 };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.getModerationQueue();

      expect(mockQueryBus.execute).toHaveBeenCalledWith(expect.any(GetModerationQueueQuery));
      const query = mockQueryBus.execute.mock.calls[0]![0] as GetModerationQueueQuery;
      expect(query.page).toBe(1);
      expect(query.limit).toBe(20);
      expect(result).toEqual(expected);
    });

    it('parses page and limit from strings', async () => {
      mockQueryBus.execute.mockResolvedValue({ data: [] });

      await controller.getModerationQueue('3', '50');

      const query = mockQueryBus.execute.mock.calls[0]![0] as GetModerationQueueQuery;
      expect(query.page).toBe(3);
      expect(query.limit).toBe(50);
    });
  });

  describe('POST /admin/moderation/approve — approveListing', () => {
    it('dispatches ApproveListingCommand', async () => {
      const expected = { listingId: 'l-1', status: 'ACTIVE' };
      mockCommandBus.execute.mockResolvedValue(expected);

      const result = await controller.approveListing(
        { listingId: 'l-1', moderationNotes: 'OK' } as any,
        mockAdmin as any,
      );

      expect(mockCommandBus.execute).toHaveBeenCalledWith(expect.any(ApproveListingCommand));
      const cmd = mockCommandBus.execute.mock.calls[0]![0] as ApproveListingCommand;
      expect(cmd.listingId).toBe('l-1');
      expect(cmd.adminId).toBe('admin-1');
      expect(result).toEqual(expected);
    });
  });

  describe('POST /admin/moderation/reject — rejectListing', () => {
    it('dispatches RejectListingCommand', async () => {
      const expected = { listingId: 'l-1', status: 'REJECTED' };
      mockCommandBus.execute.mockResolvedValue(expected);

      const result = await controller.rejectListing(
        { listingId: 'l-1', reason: 'Spam' } as any,
        mockAdmin as any,
      );

      expect(mockCommandBus.execute).toHaveBeenCalledWith(expect.any(RejectListingCommand));
      const cmd = mockCommandBus.execute.mock.calls[0]![0] as RejectListingCommand;
      expect(cmd.listingId).toBe('l-1');
      expect(cmd.adminId).toBe('admin-1');
      expect(cmd.reason).toBe('Spam');
      expect(result).toEqual(expected);
    });
  });

  describe('POST /admin/moderation/bulk — bulkModerate', () => {
    it('dispatches BulkModerateListingsCommand', async () => {
      const expected = { processed: 3, failed: 0 };
      mockCommandBus.execute.mockResolvedValue(expected);

      const result = await controller.bulkModerate(
        { listingIds: ['l-1', 'l-2', 'l-3'], action: 'approve', reason: 'Batch' } as any,
        mockAdmin as any,
      );

      expect(mockCommandBus.execute).toHaveBeenCalledWith(expect.any(BulkModerateListingsCommand));
      const cmd = mockCommandBus.execute.mock.calls[0]![0] as BulkModerateListingsCommand;
      expect(cmd.listingIds).toEqual(['l-1', 'l-2', 'l-3']);
      expect(cmd.adminId).toBe('admin-1');
      expect(cmd.action).toBe('approve');
      expect(result).toEqual(expected);
    });
  });

  describe('GET /admin/kyc — getKycQueue', () => {
    it('dispatches GetKycQueueQuery with defaults', async () => {
      const expected = { data: [], total: 0, page: 1, limit: 20, totalPages: 0 };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.getKycQueue();

      expect(mockQueryBus.execute).toHaveBeenCalledWith(expect.any(GetKycQueueQuery));
      const query = mockQueryBus.execute.mock.calls[0]![0] as GetKycQueueQuery;
      expect(query.page).toBe(1);
      expect(query.limit).toBe(20);
      expect(result).toEqual(expected);
    });
  });

  describe('POST /admin/kyc/approve — approveKyc', () => {
    it('dispatches ApproveKycCommand', async () => {
      const expected = { userId: 'u-1', kycStatus: 'VERIFIED' };
      mockCommandBus.execute.mockResolvedValue(expected);

      const result = await controller.approveKyc(
        { userId: 'u-1', comments: 'Good docs' } as any,
        mockAdmin as any,
      );

      expect(mockCommandBus.execute).toHaveBeenCalledWith(expect.any(ApproveKycCommand));
      const cmd = mockCommandBus.execute.mock.calls[0]![0] as ApproveKycCommand;
      expect(cmd.userId).toBe('u-1');
      expect(cmd.adminId).toBe('admin-1');
      expect(result).toEqual(expected);
    });
  });

  describe('POST /admin/kyc/reject — rejectKyc', () => {
    it('dispatches RejectKycCommand', async () => {
      const expected = { userId: 'u-1', kycStatus: 'REJECTED' };
      mockCommandBus.execute.mockResolvedValue(expected);

      const result = await controller.rejectKyc(
        { userId: 'u-1', reason: 'Giấy tờ không hợp lệ' } as any,
        mockAdmin as any,
      );

      expect(mockCommandBus.execute).toHaveBeenCalledWith(expect.any(RejectKycCommand));
      const cmd = mockCommandBus.execute.mock.calls[0]![0] as RejectKycCommand;
      expect(cmd.userId).toBe('u-1');
      expect(cmd.adminId).toBe('admin-1');
      expect(cmd.reason).toBe('Giấy tờ không hợp lệ');
      expect(result).toEqual(expected);
    });
  });
});
