import 'reflect-metadata';

import { Reflector } from '@nestjs/core';
import { Test } from '@nestjs/testing';
import request from 'supertest';
import { vi } from 'vitest';
import { AuthenticationGuard } from '../auth/AuthenticationGuard';
import { AuthorizationGuard } from '../auth/AuthorizationGuard';
import type { AuthorizationService } from '../auth/AuthorizationService';
import { FeatureAvailabilityGuard } from '../policy/FeatureAvailabilityGuard';
import type { PolicyService, PolicySnapshot } from '../policy/PolicyService';
import { DashboardController } from './DashboardController';
import { DashboardService } from './DashboardService';
import { DashboardOverviewDTO } from './dtos/DashboardOverviewDTO';

describe('DashboardController', () => {
  let controller: DashboardController;
  let mockService: { getDashboardOverview: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockService = {
      getDashboardOverview: vi.fn(),
    };

    controller = new DashboardController(mockService as never);
  });

  describe('getDashboardOverview', () => {
    it('should call service.getDashboardOverview and return overview', async () => {
      const driverId = 'driver-123';
      const overview: DashboardOverviewDTO = {
        currentDriver: null,
        myUpcomingRaces: [],
        otherUpcomingRaces: [],
        upcomingRaces: [],
        activeLeaguesCount: 5,
        nextRace: null,
        recentResults: [],
        leagueStandingsSummaries: [],
        feedSummary: {
          notificationCount: 0,
          items: [],
        },
        friends: [],
      };
      mockService.getDashboardOverview.mockResolvedValue(overview);

      const result = await controller.getDashboardOverview(driverId, { user: { userId: driverId, primaryDriverId: driverId } });

      expect(mockService.getDashboardOverview).toHaveBeenCalledWith(driverId);
      expect(result).toEqual(overview);
    });
  });

  describe('auth guards (HTTP)', () => {
    let app: import("@nestjs/common").INestApplication;

    const sessionPort: { getCurrentSession: () => Promise<null | { token: string; user: { id: string; primaryDriverId?: string } }> } = {
      getCurrentSession: vi.fn(async () => null),
    };

    const authorizationService: Pick<AuthorizationService, 'getRolesForUser'> = {
      getRolesForUser: vi.fn(() => []),
    };

    const policyService: Pick<PolicyService, 'getSnapshot'> = {
      getSnapshot: vi.fn(async (): Promise<PolicySnapshot> => ({
        policyVersion: 1,
        operationalMode: 'normal',
        maintenanceAllowlist: { view: [], mutate: [] },
        capabilities: {},
        loadedFrom: 'defaults',
        loadedAtIso: new Date(0).toISOString(),
      })),
    };

    beforeEach(async () => {
      const module = await Test.createTestingModule({
        controllers: [DashboardController],
        providers: [
          {
            provide: DashboardService,
            useValue: {
              getDashboardOverview: vi.fn(async () => ({
                currentDriver: null,
                myUpcomingRaces: [],
                otherUpcomingRaces: [],
                upcomingRaces: [],
                activeLeaguesCount: 0,
                nextRace: null,
                recentResults: [],
                leagueStandingsSummaries: [],
                feedSummary: { notificationCount: 0, items: [] },
                friends: [],
              })),
            },
          },
        ],
      })
        .overrideProvider(DashboardController)
        .useFactory({
          factory: (dashboardService: DashboardService) => new DashboardController(dashboardService),
          inject: [DashboardService],
        })
        .compile();

      app = module.createNestApplication();

      const reflector = new Reflector();
      app.useGlobalGuards(
        new AuthenticationGuard(sessionPort as never),
        new AuthorizationGuard(reflector, authorizationService as never),
        new FeatureAvailabilityGuard(reflector, policyService as never),
      );

      await app.init();
    });

    afterEach(async () => {
      await app?.close();
      vi.clearAllMocks();
    });

    it('denies endpoint by default when not authenticated (401)', async () => {
      await request(app.getHttpServer()).get('/dashboard/overview?driverId=d1').expect(401);
    });

    it('allows endpoint when authenticated via session port', async () => {
      vi.mocked(sessionPort.getCurrentSession).mockResolvedValueOnce({
        token: 't',
        user: { id: 'user-1', primaryDriverId: 'driver-1' },
      });

      await request(app.getHttpServer()).get('/dashboard/overview?driverId=d1').expect(200);
    });
  });
});