/**
 * @fileoverview ComplianceReport repository implementation
 *
 * Concrete implementation of IComplianceReportRepository using DeepSource API.
 */

import { IComplianceReportRepository } from '../../domain/aggregates/compliance-report/compliance-report.repository.js';
import { ComplianceReport } from '../../domain/aggregates/compliance-report/compliance-report.aggregate.js';
import { ProjectKey } from '../../types/branded.js';
import { ReportType } from '../../types/report-types.js';
import {
  ComplianceReportStatus,
  ComplianceReportId,
} from '../../domain/aggregates/compliance-report/compliance-report.types.js';
import { DeepSourceClient } from '../../deepsource.js';
import { ComplianceReportMapper } from '../mappers/compliance-report.mapper.js';
import { createLogger } from '../../utils/logging/logger.js';
import { BaseDeepSourceClient } from '../../client/base-client.js';
import type { AxiosInstance } from 'axios';

const logger = createLogger('ComplianceReportRepository');

/**
 * Concrete implementation of IComplianceReportRepository using DeepSource API
 *
 * This repository provides access to ComplianceReport aggregates by fetching data
 * from the DeepSource API and mapping it to domain models.
 *
 * Note: The DeepSource API provides compliance reports for specific types
 * (OWASP_TOP_10, SANS_TOP_25, MISRA_C). This repository ensures fresh data
 * retrieval on every request as per requirements.
 */
export class ComplianceReportRepository implements IComplianceReportRepository {
  constructor(private readonly client: DeepSourceClient) {
    // client is stored for use in methods
  }

  /**
   * Helper method to get repository ID for a project
   *
   * @param projectKey - The project key
   * @returns The repository GraphQL ID
   */
  private async getRepositoryId(projectKey: ProjectKey): Promise<string> {
    const projects = await this.client.listProjects();
    const project = projects.find((p) => p.key === projectKey);
    if (!project) {
      throw new Error(`Project not found: ${projectKey}`);
    }

    // Get repository ID from runs query (similar to QualityMetricsRepository)
    try {
      const runs = await this.client.listRuns(projectKey, { first: 1 });
      if (runs.items.length > 0 && runs.items[0]?.repository?.id) {
        return runs.items[0].repository.id;
      }

      // Fallback: Direct repository query
      const repoQuery = `
        query($login: String!, $name: String!, $provider: VCSProvider!) {
          repository(login: $login, name: $name, vcsProvider: $provider) {
            id
          }
        }
      `;

      // Access protected client member via type assertion
      const clientWithAxios = this.client as unknown as BaseDeepSourceClient & {
        client: AxiosInstance;
      };
      const response = await clientWithAxios.client.post('', {
        query: repoQuery.trim(),
        variables: {
          login: project.repository.login,
          name: project.name,
          provider: project.repository.provider,
        },
      });

      if (response.data.errors) {
        throw new Error(
          `GraphQL Errors: ${response.data.errors.map((e: { message: string }) => e.message).join(', ')}`
        );
      }

      const repositoryId = response.data.data?.repository?.id;
      if (!repositoryId) {
        throw new Error(`Repository ID not found for project: ${projectKey}`);
      }

      return repositoryId;
    } catch (error) {
      logger.error('Error fetching repository ID', { projectKey, error });
      throw error;
    }
  }

  /**
   * Finds a report by composite ID
   *
   * @param id - The composite ID (projectKey:reportType)
   * @returns The report if found, null otherwise
   */
  async findById(id: string): Promise<ComplianceReport | null> {
    try {
      logger.debug('Finding compliance report by composite ID', { id });

      // Parse composite ID
      const parts = id.split(':');
      if (parts.length !== 2) {
        logger.error('Invalid composite ID format', { id });
        return null;
      }

      const [projectKey, reportType] = parts;
      return this.findByProjectAndType(projectKey as ProjectKey, reportType as ReportType);
    } catch (error) {
      logger.error('Error finding compliance report by ID', { id, error });
      throw error;
    }
  }

  /**
   * Finds a report by project and type
   *
   * @param projectKey - The project key
   * @param reportType - The report type
   * @returns The report if found, null otherwise
   */
  async findByProjectAndType(
    projectKey: ProjectKey,
    reportType: ReportType
  ): Promise<ComplianceReport | null> {
    try {
      logger.debug('Finding compliance report by project and type', {
        projectKey,
        reportType,
      });

      const repositoryId = await this.getRepositoryId(projectKey);
      const apiReport = await this.client.getComplianceReport(projectKey, reportType);

      if (!apiReport) {
        logger.debug('Compliance report not found', { projectKey, reportType });
        return null;
      }

      const domainReport = ComplianceReportMapper.toDomain(apiReport, projectKey, repositoryId);

      logger.debug('Compliance report found and mapped', {
        projectKey,
        reportType,
        id: domainReport.id,
      });

      return domainReport;
    } catch (error) {
      logger.error('Error finding compliance report by project and type', {
        projectKey,
        reportType,
        error,
      });
      throw error;
    }
  }

  /**
   * Finds the latest reports for a project
   *
   * @param projectKey - The project key
   * @returns All reports for the project, sorted by generation date (newest first)
   */
  async findLatest(projectKey: ProjectKey): Promise<ComplianceReport[]> {
    try {
      logger.debug('Finding latest compliance reports for project', { projectKey });

      const repositoryId = await this.getRepositoryId(projectKey);
      const reportTypes = [ReportType.OWASP_TOP_10, ReportType.SANS_TOP_25, ReportType.MISRA_C];
      const reports: ComplianceReport[] = [];

      // Fetch all available report types
      for (const reportType of reportTypes) {
        try {
          const apiReport = await this.client.getComplianceReport(projectKey, reportType);
          if (apiReport) {
            const domainReport = ComplianceReportMapper.toDomain(
              apiReport,
              projectKey,
              repositoryId
            );
            reports.push(domainReport);
          }
        } catch (error) {
          // Log but don't fail - some report types may not be available
          logger.debug('Report type not available', { projectKey, reportType, error });
        }
      }

      // Sort by generation date (newest first)
      reports.sort((a, b) => b.generatedAt.getTime() - a.generatedAt.getTime());

      logger.debug('Latest compliance reports found', {
        projectKey,
        count: reports.length,
      });

      return reports;
    } catch (error) {
      logger.error('Error finding latest compliance reports', { projectKey, error });
      throw error;
    }
  }

  /**
   * Finds reports by status
   *
   * @param projectKey - The project key
   * @param status - The report status
   * @returns Reports matching the status
   */
  async findByStatus(
    projectKey: ProjectKey,
    status: ComplianceReportStatus
  ): Promise<ComplianceReport[]> {
    try {
      logger.debug('Finding compliance reports by status', { projectKey, status });

      const allReports = await this.findLatest(projectKey);
      const statusReports = allReports.filter((report) => report.status === status);

      logger.debug('Status-filtered compliance reports found', {
        projectKey,
        status,
        count: statusReports.length,
      });

      return statusReports;
    } catch (error) {
      logger.error('Error finding reports by status', { projectKey, status, error });
      throw error;
    }
  }

  /**
   * Finds all compliant reports for a project
   *
   * @param projectKey - The project key
   * @returns Reports with compliance score >= 85%
   */
  async findCompliantReports(projectKey: ProjectKey): Promise<ComplianceReport[]> {
    try {
      logger.debug('Finding compliant reports for project', { projectKey });

      const allReports = await this.findLatest(projectKey);
      const compliantReports = allReports.filter((report) => report.isCompliant);

      logger.debug('Compliant reports found', {
        projectKey,
        count: compliantReports.length,
      });

      return compliantReports;
    } catch (error) {
      logger.error('Error finding compliant reports', { projectKey, error });
      throw error;
    }
  }

  /**
   * Finds reports with critical issues
   *
   * @param projectKey - The project key
   * @returns Reports that have critical severity issues
   */
  async findReportsWithCriticalIssues(projectKey: ProjectKey): Promise<ComplianceReport[]> {
    try {
      logger.debug('Finding reports with critical issues for project', { projectKey });

      const allReports = await this.findLatest(projectKey);
      const criticalReports = allReports.filter((report) => report.hasCriticalIssues);

      logger.debug('Reports with critical issues found', {
        projectKey,
        count: criticalReports.length,
      });

      return criticalReports;
    } catch (error) {
      logger.error('Error finding reports with critical issues', { projectKey, error });
      throw error;
    }
  }

  /**
   * Finds a report by composite ID components
   *
   * @param id - The composite ID components
   * @returns The report if found, null otherwise
   */
  async findByCompositeId(id: ComplianceReportId): Promise<ComplianceReport | null> {
    try {
      logger.debug('Finding compliance report by composite ID components', { id });

      return this.findByProjectAndType(id.projectKey, id.reportType);
    } catch (error) {
      logger.error('Error finding report by composite ID', { id, error });
      throw error;
    }
  }

  /**
   * Counts total reports for a project
   *
   * @param projectKey - The project key
   * @returns The total number of reports
   */
  async countByProject(projectKey: ProjectKey): Promise<number> {
    try {
      logger.debug('Counting compliance reports for project', { projectKey });

      const reports = await this.findLatest(projectKey);
      const count = reports.length;

      logger.debug('Compliance report count for project', { projectKey, count });
      return count;
    } catch (error) {
      logger.error('Error counting reports', { projectKey, error });
      throw error;
    }
  }

  /**
   * Counts reports by type for a project
   *
   * @param projectKey - The project key
   * @param reportType - The report type
   * @returns The number of reports of the given type
   */
  async countByType(projectKey: ProjectKey, reportType: ReportType): Promise<number> {
    try {
      logger.debug('Counting compliance reports by type', { projectKey, reportType });

      const report = await this.findByProjectAndType(projectKey, reportType);
      const count = report ? 1 : 0;

      logger.debug('Report count by type', { projectKey, reportType, count });
      return count;
    } catch (error) {
      logger.error('Error counting reports by type', { projectKey, reportType, error });
      throw error;
    }
  }

  /**
   * Checks if a report exists for a project and type
   *
   * @param projectKey - The project key
   * @param reportType - The report type
   * @returns True if a report exists, false otherwise
   */
  async exists(projectKey: ProjectKey, reportType: ReportType): Promise<boolean> {
    try {
      logger.debug('Checking if compliance report exists', { projectKey, reportType });

      const report = await this.findByProjectAndType(projectKey, reportType);
      const exists = report !== null;

      logger.debug('Compliance report existence check', { projectKey, reportType, exists });
      return exists;
    } catch (error) {
      logger.error('Error checking report existence', { projectKey, reportType, error });
      throw error;
    }
  }

  /**
   * Saves compliance report
   *
   * Note: The DeepSource API doesn't support creating or updating compliance reports.
   * Reports are generated automatically based on repository analysis.
   *
   * @param report - The report to save
   * @throws Error indicating the operation is not supported
   */
  // skipcq: JS-0105 - Repository method required by interface contract
  async save(report: ComplianceReport): Promise<void> {
    logger.warn('Attempted to save compliance report', { id: report.id });

    throw new Error(
      'Save operation is not supported by DeepSource API. ' +
        'Compliance reports are generated automatically based on repository analysis.'
    );
  }

  /**
   * Deletes compliance report
   *
   * Note: The DeepSource API doesn't support deleting compliance reports.
   * Reports are managed automatically based on repository configuration.
   *
   * @param id - The report ID to delete
   * @throws Error indicating the operation is not supported
   */
  // skipcq: JS-0105 - Repository method required by interface contract
  async delete(id: string): Promise<void> {
    logger.warn('Attempted to delete compliance report', { id });

    throw new Error(
      'Delete operation is not supported by DeepSource API. ' +
        'Compliance reports are managed automatically based on repository configuration.'
    );
  }
}
