import { AdjustSubscriptionCommand } from '../../../application/commands/adjust-subscription/adjust-subscription.command';
import { BanUserCommand } from '../../../application/commands/ban-user/ban-user.command';
import { UpdateUserStatusCommand } from '../../../application/commands/update-user-status/update-user-status.command';
import { GetAuditLogsQuery } from '../../../application/queries/get-audit-logs/get-audit-logs.query';
import { GetDashboardStatsQuery } from '../../../application/queries/get-dashboard-stats/get-dashboard-stats.query';
import { GetRevenueStatsQuery } from '../../../application/queries/get-revenue-stats/get-revenue-stats.query';
import { GetUserDetailQuery } from '../../../application/queries/get-user-detail/get-user-detail.query';
import { GetUsersQuery } from '../../../application/queries/get-users/get-users.query';
import { AdminController } from '../admin.controller';

describe('AdminController', () => {
  let controller: AdminController;
  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 AdminController(mockCommandBus as any, mockQueryBus as any);
  });

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

      const result = await controller.getUsers({} as any);

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

    it('passes custom filters', async () => {
      mockQueryBus.execute.mockResolvedValue({ data: [] });

      await controller.getUsers({
        page: 2,
        limit: 10,
        role: 'AGENT',
        isActive: true,
        search: 'test',
      } as any);

      const query = mockQueryBus.execute.mock.calls[0]![0] as GetUsersQuery;
      expect(query.page).toBe(2);
      expect(query.limit).toBe(10);
      expect(query.role).toBe('AGENT');
      expect(query.isActive).toBe(true);
      expect(query.search).toBe('test');
    });
  });

  describe('GET /admin/users/:id — getUserDetail', () => {
    it('dispatches GetUserDetailQuery with user id', async () => {
      const expected = { id: 'user-1', fullName: 'Test User' };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.getUserDetail('user-1');

      expect(mockQueryBus.execute).toHaveBeenCalledWith(expect.any(GetUserDetailQuery));
      const query = mockQueryBus.execute.mock.calls[0]![0] as GetUserDetailQuery;
      expect(query.userId).toBe('user-1');
      expect(result).toEqual(expected);
    });
  });

  describe('PATCH /admin/users/status — updateUserStatus', () => {
    it('dispatches UpdateUserStatusCommand', async () => {
      const expected = { userId: 'user-1', isActive: false };
      mockCommandBus.execute.mockResolvedValue(expected);

      const result = await controller.updateUserStatus(
        { userId: 'user-1', isActive: false, reason: 'Vi phạm' } as any,
        mockAdmin as any,
      );

      expect(mockCommandBus.execute).toHaveBeenCalledWith(expect.any(UpdateUserStatusCommand));
      const cmd = mockCommandBus.execute.mock.calls[0]![0] as UpdateUserStatusCommand;
      expect(cmd.userId).toBe('user-1');
      expect(cmd.adminId).toBe('admin-1');
      expect(cmd.isActive).toBe(false);
      expect(cmd.reason).toBe('Vi phạm');
      expect(result).toEqual(expected);
    });
  });

  describe('POST /admin/users/ban — banUser', () => {
    it('dispatches BanUserCommand', async () => {
      const expected = { userId: 'user-1', isBanned: true };
      mockCommandBus.execute.mockResolvedValue(expected);

      const result = await controller.banUser(
        { userId: 'user-1', reason: 'Spam' } as any,
        mockAdmin as any,
      );

      expect(mockCommandBus.execute).toHaveBeenCalledWith(expect.any(BanUserCommand));
      const cmd = mockCommandBus.execute.mock.calls[0]![0] as BanUserCommand;
      expect(cmd.userId).toBe('user-1');
      expect(cmd.adminId).toBe('admin-1');
      expect(cmd.reason).toBe('Spam');
      expect(cmd.unban).toBe(false);
      expect(result).toEqual(expected);
    });

    it('passes unban flag when set', async () => {
      mockCommandBus.execute.mockResolvedValue({ userId: 'user-1', isBanned: false });

      await controller.banUser(
        { userId: 'user-1', reason: 'Ân xá', unban: true } as any,
        mockAdmin as any,
      );

      const cmd = mockCommandBus.execute.mock.calls[0]![0] as BanUserCommand;
      expect(cmd.unban).toBe(true);
    });
  });

  describe('POST /admin/subscriptions/adjust — adjustSubscription', () => {
    it('dispatches AdjustSubscriptionCommand', async () => {
      const expected = { userId: 'user-1', newPlanTier: 'PREMIUM' };
      mockCommandBus.execute.mockResolvedValue(expected);

      const result = await controller.adjustSubscription(
        { userId: 'user-1', newPlanTier: 'PREMIUM', reason: 'Upgrade' } as any,
        mockAdmin as any,
      );

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

  describe('GET /admin/dashboard — getDashboardStats', () => {
    it('dispatches GetDashboardStatsQuery', async () => {
      const expected = { totalUsers: 100, totalListings: 50 };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.getDashboardStats();

      expect(mockQueryBus.execute).toHaveBeenCalledWith(expect.any(GetDashboardStatsQuery));
      expect(result).toEqual(expected);
    });
  });

  describe('GET /admin/revenue — getRevenueStats', () => {
    it('dispatches GetRevenueStatsQuery with parsed dates', async () => {
      const expected = [{ period: '2026-01', totalRevenue: 100_000_000n }];
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.getRevenueStats({
        startDate: '2026-01-01',
        endDate: '2026-01-31',
        groupBy: 'day',
      } as any);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(expect.any(GetRevenueStatsQuery));
      const query = mockQueryBus.execute.mock.calls[0]![0] as GetRevenueStatsQuery;
      expect(query.startDate).toEqual(new Date('2026-01-01'));
      expect(query.endDate).toEqual(new Date('2026-01-31'));
      expect(query.groupBy).toBe('day');
      expect(result).toEqual(expected);
    });

    it('defaults groupBy to month', async () => {
      mockQueryBus.execute.mockResolvedValue([]);

      await controller.getRevenueStats({
        startDate: '2026-01-01',
        endDate: '2026-12-31',
      } as any);

      const query = mockQueryBus.execute.mock.calls[0]![0] as GetRevenueStatsQuery;
      expect(query.groupBy).toBe('month');
    });
  });

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

      const result = await controller.getAuditLogs({} as any);

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

    it('passes all filter parameters', async () => {
      mockQueryBus.execute.mockResolvedValue({ data: [] });

      await controller.getAuditLogs({
        page: 3,
        limit: 50,
        action: 'user.banned',
        actorId: 'admin-1',
        targetId: 'user-1',
        targetType: 'user',
        startDate: '2026-01-01',
        endDate: '2026-12-31',
      } as any);

      const query = mockQueryBus.execute.mock.calls[0]![0] as GetAuditLogsQuery;
      expect(query.page).toBe(3);
      expect(query.limit).toBe(50);
      expect(query.action).toBe('user.banned');
      expect(query.actorId).toBe('admin-1');
      expect(query.targetId).toBe('user-1');
      expect(query.targetType).toBe('user');
      expect(query.startDate).toEqual(new Date('2026-01-01'));
      expect(query.endDate).toEqual(new Date('2026-12-31'));
    });
  });
});
