import { Test, TestingModule } from '@nestjs/testing';
import { LaravelApiController } from './laravel-api.controller';
import { LaravelApiService } from './laravel-api.service';
import { AuthenticatedUser } from '../auth/interfaces/jwt-payload.interface';
import { ApiKeyGuard } from '../common/guards/api-key/api-key.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { UserJwtGuard } from '../auth/guards/user-jwt.guard';
import { ClientJwtGuard } from '../auth/guards/client-jwt.guard';
import { DevOnlyGuard } from '../common/guards/dev-only/dev-only.guard';

describe('LaravelApiController', () => {
  let controller: LaravelApiController;
  let service: LaravelApiService;

  const mockUser: AuthenticatedUser = {
    id: 'user_123',
    type: 'user',
    payload: {
      sub: 'user_123',
      iss: 'test-issuer',
      prv: 'users',
      iat: 1640995200,
      exp: 1672531200,
      nbf: 1640995200,
      jti: 'jwt-id-123',
    },
    tokenData: { userId: 'user_123' },
    expiresAt: new Date('2024-12-31'),
    issuedAt: new Date('2024-01-01'),
    rawToken: 'test-token',
  };

  const mockClient: AuthenticatedUser = {
    id: 'client_123',
    type: 'client',
    payload: {
      sub: 'client_123',
      iss: 'test-issuer',
      prv: 'hash123',
      iat: 1640995200,
      exp: 1672531200,
      nbf: 1640995200,
      jti: 'jwt-id-456',
    },
    tokenData: { clientId: 'client_123' },
    expiresAt: new Date('2024-12-31'),
    issuedAt: new Date('2024-01-01'),
    rawToken: 'client-token',
  };

  beforeEach(async () => {
    const mockService = {
      validateTokenWithLaravel: jest.fn(),
      getUserFromLaravel: jest.fn(),
      getClientFromLaravel: jest.fn(),
      proxyRequest: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      controllers: [LaravelApiController],
      providers: [
        {
          provide: LaravelApiService,
          useValue: mockService,
        },
      ],
    })
      .overrideGuard(ApiKeyGuard)
      .useValue({ canActivate: () => true })
      .overrideGuard(JwtAuthGuard)
      .useValue({ canActivate: () => true })
      .overrideGuard(UserJwtGuard)
      .useValue({ canActivate: () => true })
      .overrideGuard(ClientJwtGuard)
      .useValue({ canActivate: () => true })
      .overrideGuard(DevOnlyGuard)
      .useValue({ canActivate: () => true })
      .compile();

    controller = module.get<LaravelApiController>(LaravelApiController);
    service = module.get<LaravelApiService>(LaravelApiService);
  });

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });

  describe('checkHealth', () => {
    it('should return health check with user data', () => {
      const result = controller.checkHealth(mockUser, 'test-token');

      expect(result).toEqual({
        message: 'Integração BFF -> Laravel API funcionando',
        user: {
          id: 'user_123',
          type: 'user',
          tokenData: { userId: 'user_123' },
        },
        tokenAvailable: true,
      });
    });

    it('should handle missing token', () => {
      const result = controller.checkHealth(mockUser, '');

      expect(result.tokenAvailable).toBe(false);
    });
  });

  describe('validateToken', () => {
    it('should validate token successfully', async () => {
      const mockLaravelResponse = {
        data: { id: 1, name: 'John Doe' },
        status: 200,
        headers: {},
      };

      jest
        .spyOn(service, 'validateTokenWithLaravel')
        .mockResolvedValue(mockLaravelResponse);

      const result = await controller.validateToken('valid-token', mockUser);

      expect(result).toEqual({
        valid: true,
        bffUser: mockUser,
        laravelUser: mockLaravelResponse,
        message: 'Token válido em ambos os sistemas',
      });
    });

    it('should handle token validation failure', async () => {
      jest
        .spyOn(service, 'validateTokenWithLaravel')
        .mockRejectedValue(new Error('Token inválido'));

      const result = await controller.validateToken('invalid-token', mockUser);

      expect(result).toEqual({
        valid: false,
        error: 'Token inválido',
        bffUser: mockUser,
      });
    });

    it('should handle unknown error', async () => {
      jest
        .spyOn(service, 'validateTokenWithLaravel')
        .mockRejectedValue('Unknown error');

      const result = await controller.validateToken('invalid-token', mockUser);

      expect(result).toEqual({
        valid: false,
        error: 'Erro desconhecido',
        bffUser: mockUser,
      });
    });
  });

  describe('getUserProfile', () => {
    it('should get user profile from Laravel API', async () => {
      const mockLaravelResponse = {
        data: { id: 1, name: 'John Doe', email: 'john@example.com' },
        status: 200,
        headers: {},
      };

      jest
        .spyOn(service, 'getUserFromLaravel')
        .mockResolvedValue(mockLaravelResponse);

      const result = await controller.getUserProfile(
        'user-token',
        'user-uuid-123',
        'john@example.com',
        'John Doe',
      );

      expect(result).toEqual({
        source: 'Laravel API',
        tokenData: {
          uuid: 'user-uuid-123',
          email: 'john@example.com',
          name: 'John Doe',
        },
        laravelData: mockLaravelResponse.data,
      });
    });
  });

  describe('getClientProfile', () => {
    it('should get client profile from Laravel API', async () => {
      const mockLaravelResponse = {
        data: { id: 1, name: 'Client Name', type: 'client' },
        status: 200,
        headers: {},
      };

      const tokenData = { clientId: 'client_123' };

      jest
        .spyOn(service, 'getClientFromLaravel')
        .mockResolvedValue(mockLaravelResponse);

      const result = await controller.getClientProfile(
        'client-token',
        tokenData,
      );

      expect(result).toEqual({
        source: 'Laravel API',
        tokenData,
        laravelData: mockLaravelResponse.data,
      });
    });
  });

  describe('proxyRequest', () => {
    it('should proxy GET request', async () => {
      const mockResponse = {
        data: { result: 'success' },
        status: 200,
        headers: {},
      };

      jest.spyOn(service, 'proxyRequest').mockResolvedValue(mockResponse);

      const result = await controller.proxyRequest(
        'test-endpoint',
        { method: 'GET' },
        'proxy-token',
        mockUser,
      );

      expect(result).toEqual({
        source: 'Laravel API via BFF Proxy',
        endpoint: '/test-endpoint',
        method: 'GET',
        bffUser: {
          id: 'user_123',
          type: 'user',
          tokenData: { userId: 'user_123' },
        },
        response: mockResponse.data,
        status: 200,
      });
    });

    it('should proxy POST request with data', async () => {
      const mockResponse = {
        data: { created: true, id: 123 },
        status: 201,
        headers: {},
      };

      const requestBody = {
        method: 'POST' as const,
        data: { name: 'New Item' },
        params: { category: 'test' },
        headers: { 'X-Custom': 'header' },
      };

      jest.spyOn(service, 'proxyRequest').mockResolvedValue(mockResponse);

      const result = await controller.proxyRequest(
        'test-endpoint',
        requestBody,
        'proxy-token',
        mockUser,
      );

      expect(service.proxyRequest).toHaveBeenCalledWith(
        'proxy-token',
        'POST',
        '/test-endpoint',
        { name: 'New Item' },
        { category: 'test' },
        { 'X-Custom': 'header' },
      );

      expect(result.method).toBe('POST');
      expect(result.response).toEqual(mockResponse.data);
    });

    it('should default to GET method when not specified', async () => {
      const mockResponse = {
        data: { result: 'success' },
        status: 200,
        headers: {},
      };

      jest.spyOn(service, 'proxyRequest').mockResolvedValue(mockResponse);

      const result = await controller.proxyRequest(
        'test-endpoint',
        {},
        'proxy-token',
        mockUser,
      );

      expect(service.proxyRequest).toHaveBeenCalledWith(
        'proxy-token',
        'GET',
        '/test-endpoint',
        undefined,
        undefined,
        undefined,
      );

      expect(result.method).toBe('GET');
    });
  });

  describe('debugToken', () => {
    it('should return complete token debug information', () => {
      const result = controller.debugToken(
        mockUser,
        'test-token',
        { userId: 'user_123' },
        { sub: 'user_123', iss: 'test-issuer' },
        'user-uuid-123',
        'john@example.com',
        'John Doe',
      );

      expect(result).toEqual({
        message: 'Debug completo do token JWT',
        data: {
          user: {
            id: 'user_123',
            type: 'user',
            expiresAt: mockUser.expiresAt,
            issuedAt: mockUser.issuedAt,
          },
          rawToken: 'test-token',
          tokenData: { userId: 'user_123' },
          jwtPayload: { sub: 'user_123', iss: 'test-issuer' },
          extractedFields: {
            uuid: 'user-uuid-123',
            email: 'john@example.com',
            name: 'John Doe',
          },
          tokenLength: 10,
          tokenParts: 1,
        },
      });
    });

    it('should handle missing token', () => {
      const result = controller.debugToken(mockUser, '', {}, {}, '', '', '');

      expect(result.data.tokenLength).toBe(0);
      expect(result.data.tokenParts).toBe(0);
    });
  });

  describe('testUserEndpoint', () => {
    it('should test user endpoint', async () => {
      const mockResponse = {
        data: { id: 1, name: 'John Doe' },
        status: 200,
        headers: {},
      };

      jest.spyOn(service, 'proxyRequest').mockResolvedValue(mockResponse);

      const result = await controller.testUserEndpoint('user-token');

      expect(service.proxyRequest).toHaveBeenCalledWith(
        'user-token',
        'GET',
        '/api/user',
      );

      expect(result).toEqual({
        message: 'Teste de endpoint de usuário da API Laravel',
        response: mockResponse.data,
        status: 200,
      });
    });
  });

  describe('testClientEndpoint', () => {
    it('should test client endpoint', async () => {
      const mockResponse = {
        data: { id: 1, name: 'Client Name' },
        status: 200,
        headers: {},
      };

      jest.spyOn(service, 'proxyRequest').mockResolvedValue(mockResponse);

      const result = await controller.testClientEndpoint('client-token');

      expect(service.proxyRequest).toHaveBeenCalledWith(
        'client-token',
        'GET',
        '/api/client/me',
      );

      expect(result).toEqual({
        message: 'Teste de endpoint de client da API Laravel',
        response: mockResponse.data,
        status: 200,
      });
    });
  });
});
