import 'reflect-metadata';

import { ValidationPipe } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Test } from '@nestjs/testing';
import request from 'supertest';
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';

import { requestContextMiddleware } from '@adapters/http/RequestContext';
import { AuthenticationGuard } from '../auth/AuthenticationGuard';
import { AuthorizationGuard } from '../auth/AuthorizationGuard';
import { IDENTITY_SESSION_PORT_TOKEN } from '../auth/AuthProviders';
import { FeatureAvailabilityGuard } from '../policy/FeatureAvailabilityGuard';

describe('League roster admin read (HTTP, league-scoped)', () => {
  const originalEnv = { ...process.env };

  let app: any;

  beforeAll(async () => {
    vi.resetModules();

    process.env.GRIDPILOT_API_PERSISTENCE = 'inmemory';
    process.env.GRIDPILOT_API_BOOTSTRAP = 'true';
    delete process.env.DATABASE_URL;

    const { AppModule } = await import('../../app.module');

    const module = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = module.createNestApplication();

    // Ensure AsyncLocalStorage request context is present for getActorFromRequestContext()
    app.use(requestContextMiddleware);

    app.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        forbidNonWhitelisted: true,
        transform: true,
      }),
    );

    const reflector = new Reflector();
    const sessionPort = module.get(IDENTITY_SESSION_PORT_TOKEN);

    const authorizationService = {
      getRolesForUser: () => [],
    };

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

    app.useGlobalGuards(
      new AuthenticationGuard(sessionPort as any),
      new AuthorizationGuard(reflector, authorizationService as any),
      new FeatureAvailabilityGuard(reflector, policyService as any),
    );

    await app.init();
  }, 20_000);

  afterAll(async () => {
    await app?.close();

    process.env = originalEnv;
    vi.restoreAllMocks();
  });

  it('rejects unauthenticated actor (401)', async () => {
    await request(app.getHttpServer()).get('/leagues/league-5/admin/roster/members').expect(401);

    await request(app.getHttpServer()).get('/leagues/league-5/admin/roster/join-requests').expect(401);
  });

  it('rejects authenticated non-admin actor (403)', async () => {
    const agent = request.agent(app.getHttpServer());

    await agent
      .post('/auth/signup')
      .send({ email: 'roster-read-user@gridpilot.local', password: 'pw1', displayName: 'Roster Read User' })
      .expect(201);

    await agent.get('/leagues/league-5/admin/roster/members').expect(403);
    await agent.get('/leagues/league-5/admin/roster/join-requests').expect(403);
  });

  it('returns roster members with stable fields (happy path)', async () => {
    const agent = request.agent(app.getHttpServer());

    await agent.post('/auth/login').send({ email: 'admin@gridpilot.local', password: 'admin123' }).expect(201);

    const res = await agent.get('/leagues/league-5/admin/roster/members').expect(200);

    expect(res.body).toEqual(expect.any(Array));
    expect(res.body.length).toBeGreaterThan(0);

    const first = res.body[0] as any;
    expect(first).toMatchObject({
      driverId: expect.any(String),
      role: expect.any(String),
      joinedAt: expect.any(String),
      driver: expect.any(Object),
    });

    expect(first.driver).toMatchObject({
      id: expect.any(String),
      name: expect.any(String),
      country: expect.any(String),
    });

    expect(['owner', 'admin', 'steward', 'member']).toContain(first.role);
  });

  it('returns join requests with stable fields (happy path)', async () => {
    const adminAgent = request.agent(app.getHttpServer());

    await adminAgent.post('/auth/login').send({ email: 'admin@gridpilot.local', password: 'admin123' }).expect(201);

    const res = await adminAgent.get('/leagues/league-5/admin/roster/join-requests').expect(200);

    expect(res.body).toEqual(expect.any(Array));

    // Seed data may or may not include join requests for a given league.
    // Validate shape on first item if present.
    if ((res.body as any[]).length > 0) {
      const first = (res.body as any[])[0];
      expect(first).toMatchObject({
        id: expect.any(String),
        leagueId: expect.any(String),
        driverId: expect.any(String),
        requestedAt: expect.any(String),
        driver: {
          id: expect.any(String),
          name: expect.any(String),
        },
      });

      if (first.message !== undefined) {
        expect(first.message).toEqual(expect.any(String));
      }
    }
  });
});