/**
 * @vitest-environment node
 */

import { vi } from 'vitest';
import { asProjectKey } from '../../types/branded';
import type { IComplianceReportRepository } from '../../domain/aggregates/compliance-report/compliance-report.repository';
import type { ComplianceReport } from '../../domain/aggregates/compliance-report/compliance-report.aggregate';
import type { Logger } from '../../utils/logging/logger';
import { ReportType } from '../../deepsource';

// Mock modules before importing the implementation
vi.mock('../../utils/logging/logger', () => {
  const mockLogger = {
    debug: globalThis.vi.fn(),
    info: globalThis.vi.fn(),
    warn: globalThis.vi.fn(),
    error: globalThis.vi.fn(),
  };
  return {
    createLogger: globalThis.vi.fn(() => mockLogger),
    __mockLogger: mockLogger, // Export for test access
  };
});

// Mock the repository and factory
vi.mock('../../infrastructure/factories/repository.factory', () => {
  const mockFindByProjectAndType = globalThis.vi.fn();
  const mockComplianceReportRepository = {
    findByProjectAndType: mockFindByProjectAndType,
  } as unknown as IComplianceReportRepository;

  const mockCreateComplianceReportRepository = globalThis.vi.fn(
    () => mockComplianceReportRepository
  );
  const mockRepositoryFactory = globalThis.vi.fn(() => ({
    createComplianceReportRepository: mockCreateComplianceReportRepository,
  }));

  return {
    RepositoryFactory: mockRepositoryFactory,
    // Export mocks for access in tests
    __mockFindByProjectAndType: mockFindByProjectAndType,
    __mockComplianceReportRepository: mockComplianceReportRepository,
    __mockCreateComplianceReportRepository: mockCreateComplianceReportRepository,
    __mockRepositoryFactory: mockRepositoryFactory,
  };
});

// Import the modules under test AFTER mocking
const { handleDeepsourceComplianceReport, createComplianceReportHandlerWithRepo } = await import(
  '../../handlers/compliance-reports'
);

// Get mocked functions
interface MockedRepositoryModule {
  __mockFindByProjectAndType: ReturnType<typeof vi.fn>;
  __mockComplianceReportRepository: Record<string, unknown>;
  __mockCreateComplianceReportRepository: ReturnType<typeof vi.fn>;
  __mockRepositoryFactory: Record<string, unknown>;
}

const mockRepositoryFactoryModule = (await import(
  '../../infrastructure/factories/repository.factory'
)) as unknown as MockedRepositoryModule;
const mockFindByProjectAndType = mockRepositoryFactoryModule.__mockFindByProjectAndType;
const mockComplianceReportRepository = mockRepositoryFactoryModule.__mockComplianceReportRepository;
const mockCreateComplianceReportRepository =
  mockRepositoryFactoryModule.__mockCreateComplianceReportRepository;
const mockRepositoryFactory = mockRepositoryFactoryModule.__mockRepositoryFactory;

// Get mocked logger
interface MockedLoggerModule {
  __mockLogger: Logger;
}
const mockLoggerModule = (await import(
  '../../utils/logging/logger'
)) as unknown as MockedLoggerModule;
const mockLogger = mockLoggerModule.__mockLogger;

describe('Compliance Reports Handler', () => {
  // Environment backup
  let originalEnv: Record<string, string | undefined>;

  beforeEach(() => {
    // Backup environment
    originalEnv = { ...process.env };
    process.env.DEEPSOURCE_API_KEY = 'test-api-key';

    // Reset mocks
    vi.clearAllMocks();
  });

  afterEach(() => {
    // Restore environment
    process.env = originalEnv;
  });

  describe('createComplianceReportHandlerWithRepo', () => {
    it('should create a handler that uses injected dependencies', async () => {
      // Mock domain compliance report
      const projectKey = asProjectKey('test-project');
      const mockReport = {
        projectKey,
        reportType: ReportType.OWASP_TOP_10,
        status: 'READY' as const,
        categories: [
          {
            name: 'A01:2021 - Broken Access Control',
            nonCompliant: { count: 1 },
            issueCount: { count: 2 },
            severity: 'MAJOR' as const,
          },
          {
            name: 'A02:2021 - Cryptographic Failures',
            nonCompliant: { count: 0 },
            issueCount: { count: 0 },
            severity: 'CRITICAL' as const,
          },
        ],
        summary: {
          complianceScore: { value: 85 },
          severityDistribution: {
            critical: { count: 0 },
            major: { count: 1 },
            info: { count: 1 },
          },
          totalIssues: { count: 2 },
        },
        trend: {
          label: 'Last 30 days',
          value: 85,
          changePercentage: 5,
          direction: 'IMPROVING' as const,
        },
        generatedAt: new Date(),
      } as ComplianceReport;

      // Set up the mock to return the report
      mockFindByProjectAndType.mockResolvedValue(mockReport);

      const handler = createComplianceReportHandlerWithRepo({
        complianceReportRepository: mockComplianceReportRepository,
        logger: mockLogger as unknown as Logger,
      });

      // Call the handler
      const result = await handler({
        projectKey: 'test-project',
        reportType: ReportType.OWASP_TOP_10,
      });

      // Verify repository was used
      expect(mockFindByProjectAndType).toHaveBeenCalledWith(projectKey, ReportType.OWASP_TOP_10);

      // Verify the response
      const parsedContent = JSON.parse(result.content[0].text);
      expect(parsedContent.title).toBe('OWASP_TOP_10 Compliance Report');
      expect(parsedContent.status).toBe('PASSING');
      expect(parsedContent.currentValue).toBe(85);
      expect(parsedContent.securityIssueStats).toHaveLength(2);
      expect(parsedContent.securityIssueStats[0].occurrence.total).toBe(2);
      expect(parsedContent.analysis.total_issues).toBe(2);
      expect(parsedContent.recommendations.actions).toContain(
        'Continue monitoring security compliance'
      );
    });

    it('should handle FAILING status with appropriate recommendations', async () => {
      // Mock domain compliance report with failing status
      const projectKey = asProjectKey('test-project');
      const mockReport = {
        projectKey,
        reportType: ReportType.SANS_TOP_25,
        status: 'ERROR' as const,
        categories: [
          {
            name: 'CWE-79 - Cross-site Scripting',
            nonCompliant: { count: 5 },
            issueCount: { count: 5 },
            severity: 'CRITICAL' as const,
          },
        ],
        summary: {
          complianceScore: { value: 45 },
          severityDistribution: {
            critical: { count: 2 },
            major: { count: 2 },
            info: { count: 1 },
          },
          totalIssues: { count: 5 },
        },
        trend: null,
        generatedAt: new Date(),
      } as ComplianceReport;

      // Set up the mock to return the report
      mockFindByProjectAndType.mockResolvedValue(mockReport);

      const handler = createComplianceReportHandlerWithRepo({
        complianceReportRepository: mockComplianceReportRepository,
        logger: mockLogger as unknown as Logger,
      });

      // Call the handler
      const result = await handler({
        projectKey: 'test-project',
        reportType: ReportType.SANS_TOP_25,
      });

      // Verify the response
      const parsedContent = JSON.parse(result.content[0].text);
      expect(parsedContent.status).toBe('FAILING');
      expect(parsedContent.analysis.critical_issues).toBe(2);
      expect(parsedContent.analysis.major_issues).toBe(2);
      expect(parsedContent.analysis.minor_issues).toBe(1);
      expect(parsedContent.recommendations.actions).toContain('Fix critical security issues first');
      expect(parsedContent.recommendations.resources[0]).toContain('SANS Top 25');
    });

    it('should handle missing report', async () => {
      // Set up the mock to return null (report not found)
      mockFindByProjectAndType.mockResolvedValue(null);

      const handler = createComplianceReportHandlerWithRepo({
        complianceReportRepository: mockComplianceReportRepository,
        logger: mockLogger as unknown as Logger,
      });

      // Call the handler
      const result = await handler({
        projectKey: 'test-project',
        reportType: ReportType.MISRA_C,
      });

      // Verify error response
      expect(result.isError).toBe(true);
      const parsedContent = JSON.parse(result.content[0].text);
      expect(parsedContent.error).toContain("Report of type 'MISRA_C' not found");
      expect(parsedContent.details).toBe('Failed to retrieve compliance report');
    });

    it('should handle errors using injected logger', async () => {
      // Set up the mock to throw an error
      const testError = new Error('Repository connection failed');
      mockFindByProjectAndType.mockRejectedValue(testError);

      const handler = createComplianceReportHandlerWithRepo({
        complianceReportRepository: mockComplianceReportRepository,
        logger: mockLogger as unknown as Logger,
      });

      // Call the handler
      const result = await handler({
        projectKey: 'test-project',
        reportType: ReportType.OWASP_TOP_10,
      });

      // Verify error was logged using injected logger
      expect(mockLogger.error).toHaveBeenCalledWith(
        'Error in handleComplianceReport',
        expect.any(Object)
      );

      // Verify error response
      expect(result.isError).toBe(true);
      const parsedContent = JSON.parse(result.content[0].text);
      expect(parsedContent).toEqual({
        error: 'Repository connection failed',
        details: 'Failed to retrieve compliance report',
      });
    });
  });

  describe('handleDeepsourceComplianceReport', () => {
    it('should throw an error if DEEPSOURCE_API_KEY is not set', async () => {
      // Unset API key
      delete process.env.DEEPSOURCE_API_KEY;

      // Call the handler and expect it to throw
      await expect(
        handleDeepsourceComplianceReport({
          projectKey: 'test-project',
          reportType: ReportType.OWASP_TOP_10,
        })
      ).rejects.toThrow('Configuration error: DeepSource API key is required but not configured');
    });

    it('should return compliance report successfully', async () => {
      // Mock domain compliance report
      const projectKey = asProjectKey('test-project');
      const mockReport = {
        projectKey,
        reportType: ReportType.OWASP_TOP_10,
        status: 'READY' as const,
        categories: [
          {
            name: 'A01:2021 - Broken Access Control',
            nonCompliant: { count: 0 },
            issueCount: { count: 0 },
            severity: 'CRITICAL' as const,
          },
        ],
        summary: {
          complianceScore: { value: 92 },
          severityDistribution: {
            critical: { count: 0 },
            major: { count: 0 },
            info: { count: 0 },
          },
          totalIssues: { count: 0 },
        },
        trend: null,
        generatedAt: new Date(),
      } as ComplianceReport;

      // Set up the mock to return the report
      mockFindByProjectAndType.mockResolvedValue(mockReport);

      // Call the handler
      const result = await handleDeepsourceComplianceReport({
        projectKey: 'test-project',
        reportType: ReportType.OWASP_TOP_10,
      });

      // Verify factory was created with the API key
      expect(mockRepositoryFactory).toHaveBeenCalledWith({ apiKey: 'test-api-key' });

      // Verify createComplianceReportRepository was called
      expect(mockCreateComplianceReportRepository).toHaveBeenCalled();

      // Verify findByProjectAndType was called
      expect(mockFindByProjectAndType).toHaveBeenCalledWith(projectKey, ReportType.OWASP_TOP_10);

      // Verify logging behavior - check that key operations were logged
      expect(mockLogger.info).toHaveBeenCalledWith(
        'Fetching compliance report from repository',
        expect.any(Object)
      );

      // Verify the response structure
      expect(result).toHaveProperty('content');
      expect(result.content).toHaveLength(1);
      expect(result.content[0]).toHaveProperty('type', 'text');

      // Parse and verify the content
      const parsedContent = JSON.parse(result.content[0].text);
      expect(parsedContent.title).toBe('OWASP_TOP_10 Compliance Report');
      expect(parsedContent.status).toBe('PASSING');
      expect(parsedContent.currentValue).toBe(92);
    });

    it('should throw error when repository fails', async () => {
      // Set up the mock to throw an error
      const testError = new Error('Repository connection failed');
      mockFindByProjectAndType.mockRejectedValue(testError);

      // Call the handler and expect it to throw
      await expect(
        handleDeepsourceComplianceReport({
          projectKey: 'test-project',
          reportType: ReportType.OWASP_TOP_10,
        })
      ).rejects.toThrow('Repository connection failed');
    });

    it('should throw error when report not found', async () => {
      // Set up the mock to return null
      mockFindByProjectAndType.mockResolvedValue(null);

      // Call the handler and expect it to throw
      await expect(
        handleDeepsourceComplianceReport({
          projectKey: 'test-project',
          reportType: ReportType.MISRA_C,
        })
      ).rejects.toThrow("Report of type 'MISRA_C' not found for project 'test-project'");
    });

    it('should handle non-Error type exceptions', async () => {
      // Set up the mock to throw a non-Error value
      mockFindByProjectAndType.mockRejectedValue('Just a string error');

      // Call the handler and expect it to throw
      await expect(
        handleDeepsourceComplianceReport({
          projectKey: 'test-project',
          reportType: ReportType.OWASP_TOP_10,
        })
      ).rejects.toThrow('Unknown error');
    });
  });
});
