import {
  INestApplication,
  UnauthorizedException,
  ValidationPipe,
  VersioningType,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Test } from '@nestjs/testing';
import * as request from 'supertest';
import { useContainer } from 'class-validator';
import { DataSource } from 'typeorm';
import { AuthController } from '../auth/auth.controller';
import { AuthService } from '../auth/auth.service';
import { RoleEnum } from '../roles/roles.enum';
import { RolesGuard } from '../roles/roles.guard';
import validationOptions from '../utils/validation-options';
import { IsNotExist } from '../utils/validators/is-not-exists.validator';
import { AdminUsersController } from './admin-users.controller';
import { AdminUsersService } from './admin-users.service';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

describe('admin user HTTP security boundary', () => {
  let app: INestApplication;
  const safeUser = {
    id: 2,
    firstName: 'Target',
    lastName: 'User',
    email: 'target@example.com',
    role: { id: RoleEnum.user, name: 'User' },
    status: { id: 1, name: 'Active' },
    assignments: [],
  };
  const adminService = {
    updateProfile: jest.fn(() => safeUser),
    updateRole: jest.fn(() => safeUser),
    updateStatus: jest.fn(() => safeUser),
    findPlayers: jest.fn(),
    assignPlayer: jest.fn(),
    unlinkPlayer: jest.fn(),
  };
  const usersService = {
    findDirectory: jest.fn(),
    findMyTeams: jest.fn(() => [
      {
        id: 11,
        firstName: 'Pat',
        lastName: 'Player',
        team: { id: 4, name: 'Alpha' },
      },
    ]),
  };
  const authService = { register: jest.fn() };

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      controllers: [AdminUsersController, UsersController, AuthController],
      providers: [
        Reflector,
        RolesGuard,
        IsNotExist,
        {
          provide: DataSource,
          useValue: {
            getRepository: () => ({ findOne: jest.fn(() => undefined) }),
          },
        },
        { provide: AdminUsersService, useValue: adminService },
        { provide: UsersService, useValue: usersService },
        { provide: AuthService, useValue: authService },
      ],
    })
      .overrideGuard(AuthGuard('jwt'))
      .useValue({
        canActivate(context) {
          const httpRequest = context.switchToHttp().getRequest();
          const token = httpRequest.headers.authorization;
          if (token === 'Bearer admin') {
            httpRequest.user = { id: 1, role: { id: RoleEnum.admin } };
            return true;
          }
          if (token === 'Bearer user') {
            httpRequest.user = { id: 7, role: { id: RoleEnum.user } };
            return true;
          }
          throw new UnauthorizedException();
        },
      })
      .compile();
    app = module.createNestApplication();
    useContainer(app, { fallbackOnErrors: true });
    app.setGlobalPrefix('api');
    app.enableVersioning({ type: VersioningType.URI });
    app.useGlobalPipes(new ValidationPipe(validationOptions));
    await app.init();
  });

  afterAll(() => app.close());
  beforeEach(() => jest.clearAllMocks());

  it('enforces JWT and current global admin role on an admin mutation', async () => {
    await request(app.getHttpServer())
      .patch('/api/v1/admin/users/2/role')
      .send({ role: RoleEnum.user })
      .expect(401);
    await request(app.getHttpServer())
      .patch('/api/v1/admin/users/2/role')
      .set('Authorization', 'Bearer user')
      .send({ role: RoleEnum.user })
      .expect(403);
    await request(app.getHttpServer())
      .patch('/api/v1/admin/users/2/role')
      .set('Authorization', 'Bearer admin')
      .send({ role: RoleEnum.user })
      .expect(200, safeUser);
  });

  it.each([true, '1'])(
    'rejects non-numeric role JSON at the HTTP validation boundary: %p',
    async (role) => {
      await request(app.getHttpServer())
        .patch('/api/v1/admin/users/2/role')
        .set('Authorization', 'Bearer admin')
        .send({ role })
        .expect(422);
      expect(adminService.updateRole).not.toHaveBeenCalled();
    },
  );

  it('strips public player assignment input from registration', async () => {
    await request(app.getHttpServer())
      .post('/api/v1/auth/email/register')
      .send({
        email: 'new@example.com',
        password: 'password',
        firstName: 'New',
        lastName: 'User',
        linkPlayerId: 101,
      })
      .expect(201);

    expect(authService.register).toHaveBeenCalledWith({
      email: 'new@example.com',
      password: 'password',
      firstName: 'New',
      lastName: 'User',
    });
  });

  it('restricts team bootstrap to self and serializes only the safe projection', async () => {
    await request(app.getHttpServer())
      .get('/api/v1/users/8/teams')
      .set('Authorization', 'Bearer user')
      .expect(403);
    const response = await request(app.getHttpServer())
      .get('/api/v1/users/7/teams')
      .set('Authorization', 'Bearer user')
      .expect(200);

    expect(response.body).toEqual([
      {
        id: 11,
        firstName: 'Pat',
        lastName: 'Player',
        team: { id: 4, name: 'Alpha' },
      },
    ]);
    expect(JSON.stringify(response.body)).not.toMatch(
      /provider|socialId|password|hash|balance|createdAt|updatedAt/i,
    );
  });
});
