import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import {
  BadGatewayException,
  ForbiddenException,
  UnauthorizedException,
  NotFoundException,
} from '@nestjs/common';
import axios from 'axios';

// Mock axios before importing the service
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

import { AtlasProxyService } from './atlas-proxy.service';

describe('AtlasProxyService', () => {
  let service: AtlasProxyService;
  let mockHttpInstance: any;

  beforeEach(async () => {
    // Reset mocks
    jest.clearAllMocks();

    // Setup mock HTTP instance
    mockHttpInstance = {
      request: jest.fn(),
      interceptors: {
        request: { use: jest.fn() },
        response: { use: jest.fn() },
      },
    };

    mockedAxios.create.mockReturnValue(mockHttpInstance);

    const mockConfigService = {
      get: jest.fn((key: string) => {
        const config: Record<string, string> = {
          ATLAS_API_URL: 'http://localhost:3001',
          ATLAS_API_ORIGIN: 'http://localhost:3001',
          BFF_TO_ATLAS_SERVICE_KEY: 'pf_sk_live_test1234.secretpart',
        };
        return config[key];
      }),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AtlasProxyService,
        {
          provide: ConfigService,
          useValue: mockConfigService,
        },
      ],
    }).compile();

    service = module.get(AtlasProxyService);
  });

  describe('forwardRequest', () => {
    it('should forward a GET request to Atlas', async () => {
      const mockResponse = { data: { videos: [] } };
      mockHttpInstance.request.mockResolvedValueOnce(mockResponse);

      const result = await service.forwardRequest('GET', '/platform-videos');

      expect(result).toEqual({ videos: [] });
      expect(mockHttpInstance.request).toHaveBeenCalled();
    });

    it('should forward a PUT request with body to Atlas', async () => {
      const body = { name: 'Updated' };
      const mockResponse = { data: { id: 'client_123', name: 'Updated' } };
      mockHttpInstance.request.mockResolvedValueOnce(mockResponse);

      const result = await service.forwardRequest(
        'PUT',
        '/clients/stripe_123',
        body,
      );

      expect(result).toEqual({ id: 'client_123', name: 'Updated' });
    });

    it('should forward a POST request with body to Atlas', async () => {
      const body = { businessName: 'My Business' };
      const mockResponse = { data: { id: 'client_123' } };
      mockHttpInstance.request.mockResolvedValueOnce(mockResponse);

      const result = await service.forwardRequest(
        'POST',
        '/clients/onboarding-business',
        body,
      );

      expect(result).toEqual({ id: 'client_123' });
    });

    it('should throw BadGatewayException on network error', async () => {
      mockHttpInstance.request.mockRejectedValueOnce(
        new Error('Network error'),
      );

      await expect(
        service.forwardRequest('GET', '/platform-videos'),
      ).rejects.toThrow(BadGatewayException);
    });

    it('should throw NotFoundException for 404 response', async () => {
      const error = new Error('Not found') as any;
      error.response = {
        status: 404,
        data: { message: 'Not found' },
      };
      mockHttpInstance.request.mockRejectedValueOnce(error);

      await expect(
        service.forwardRequest('GET', '/platform-videos'),
      ).rejects.toThrow(NotFoundException);
    });

    it('should throw UnauthorizedException for 401 response', async () => {
      const error = new Error('Unauthorized') as any;
      error.response = {
        status: 401,
        data: { message: 'Unauthorized' },
      };
      mockHttpInstance.request.mockRejectedValueOnce(error);

      await expect(
        service.forwardRequest('GET', '/platform-videos'),
      ).rejects.toThrow(UnauthorizedException);
    });

    it('should throw ForbiddenException for 403 response (preserve forbidden semantics)', async () => {
      const error = new Error('Forbidden') as any;
      error.response = {
        status: 403,
        data: { message: 'Forbidden' },
      };
      mockHttpInstance.request.mockRejectedValueOnce(error);

      await expect(
        service.forwardRequest('GET', '/platform-videos'),
      ).rejects.toThrow(ForbiddenException);
    });

    it('should present the X-Planfi-Service-Key header in the request', async () => {
      mockHttpInstance.request.mockResolvedValueOnce({
        data: { videos: [] },
      });

      await service.forwardRequest('GET', '/platform-videos');

      expect(mockHttpInstance.request).toHaveBeenCalledWith(
        expect.objectContaining({
          method: 'GET',
          url: '/platform-videos',
          headers: expect.objectContaining({
            'X-Planfi-Service-Key': 'pf_sk_live_test1234.secretpart',
            Origin: expect.any(String),
          }),
        }),
      );
    });

    it('should avoid duplicating /api when ATLAS_API_URL already includes it', async () => {
      const scopedHttpInstance = {
        request: jest.fn().mockResolvedValueOnce({ data: { data: [] } }),
        interceptors: {
          request: { use: jest.fn() },
          response: { use: jest.fn() },
        },
      };
      mockedAxios.create.mockReturnValueOnce(scopedHttpInstance as any);

      const module: TestingModule = await Test.createTestingModule({
        providers: [
          AtlasProxyService,
          {
            provide: ConfigService,
            useValue: {
              get: jest.fn((key: string) => {
                const config: Record<string, string> = {
                  ATLAS_API_URL: 'http://localhost:3001/api',
                  ATLAS_API_ORIGIN: 'http://localhost:3001',
                  BFF_TO_ATLAS_SERVICE_KEY: 'pf_sk_live_test1234.secretpart',
                };
                return config[key];
              }),
            },
          },
        ],
      }).compile();

      const scopedService = module.get(AtlasProxyService);
      await scopedService.forwardRequest(
        'GET',
        '/api/atlas/internal/automation-runtime/by-trigger',
      );

      expect(scopedHttpInstance.request).toHaveBeenCalledWith(
        expect.objectContaining({
          method: 'GET',
          url: '/atlas/internal/automation-runtime/by-trigger',
        }),
      );
    });
  });
});
