import type { IReportService } from '../../interfaces/services/report-service';
import type { DetailedReport, CombinedReport } from '../../interfaces/core/types';
import type { ReportSummary } from '../../interfaces/use-cases/report-use-cases';
import type { IReportRepository } from '../../interfaces/repositories/report-repository';
import type { ISessionMetaRepository } from '../../interfaces/repositories/session-meta-repository';
import type { IUnifiedEventBus } from '../../interfaces/events/unified-event-bus';

export class ReportService implements IReportService {
  constructor(
    private reportRepository: IReportRepository,
    private sessionRepository: ISessionMetaRepository,
    private eventBus: IUnifiedEventBus
  ) {}

  getSessionReport(label: string): DetailedReport | null {
    const sessionMeta = this.sessionRepository.findByLabel(label);
    if (!sessionMeta) return null;

    const reportData = this.reportRepository.getReportData(sessionMeta.resultKey);
    if (!reportData) return null;

    const detailedReport: DetailedReport = {
      data: reportData,
      stats: reportData.stats || { tests: 0, passes: 0, failures: 0, pending: 0 },
      timestamp: reportData.timestamp ? new Date(reportData.timestamp) : new Date(),
      sessionLabel: label,
      size: JSON.stringify(reportData).length
    };

    return detailedReport;
  }

  async waitForSessionReport(label: string, timeoutMs: number = 30000): Promise<DetailedReport> {
    const sessionMeta = this.sessionRepository.findByLabel(label);
    if (!sessionMeta) {
      throw new Error(`Session '${label}' not found`);
    }

    const reportData = await this.reportRepository.waitForReport(sessionMeta.resultKey, timeoutMs);
    
    return {
      data: reportData,
      stats: reportData.stats || { tests: 0, passes: 0, failures: 0, pending: 0 },
      timestamp: reportData.timestamp ? new Date(reportData.timestamp) : new Date(),
      sessionLabel: label,
      size: JSON.stringify(reportData).length
    };
  }

  hasSessionReport(label: string): boolean {
    const sessionMeta = this.sessionRepository.findByLabel(label);
    if (!sessionMeta) return false;

    return this.reportRepository.hasReport(sessionMeta.resultKey);
  }

  generateCombinedReport(): CombinedReport {
    const allSessions = this.sessionRepository.findAll();
    const sessions: Record<string, DetailedReport> = {};
    
    let totalTests = 0;
    let totalPasses = 0;
    let totalFailures = 0;
    let totalPending = 0;
    let successfulSessions = 0;
    let failedSessions = 0;

    for (const sessionMeta of allSessions) {
      const report = this.getSessionReport(sessionMeta.label);
      if (report) {
        sessions[sessionMeta.label] = report;
        
        totalTests += report.stats.tests;
        totalPasses += report.stats.passes;
        totalFailures += report.stats.failures;
        totalPending += report.stats.pending;
        
        if (report.stats.failures === 0) {
          successfulSessions++;
        } else {
          failedSessions++;
        }
      }
    }

    const combinedReport: CombinedReport = {
      generatedAt: new Date().toISOString(),
      summary: {
        totalTests,
        totalPasses,
        totalFailures,
        totalPending,
        totalSessions: allSessions.length,
        successfulSessions,
        failedSessions
      },
      sessions
    };

    // Emit report generated event
    this.eventBus.emitRunnerEvent({
      type: 'runner:combined-report-generated',
      timestamp: new Date(),
      data: {
        reportType: 'combined',
        totalSessions: allSessions.length,
        reportSize: JSON.stringify(combinedReport).length,
        generatedAt: new Date(),
        summary: combinedReport.summary
      }
    });

    return combinedReport;
  }

  async getCombinedReportAsync(): Promise<CombinedReport> {
    // For now, just return the sync version
    // In the future, this could wait for all reports to be ready
    return this.generateCombinedReport();
  }

  watchSessionReport(label: string, callback: (report: DetailedReport) => void): () => void {
    const sessionMeta = this.sessionRepository.findByLabel(label);
    if (!sessionMeta) {
      throw new Error(`Session '${label}' not found`);
    }

    return this.reportRepository.watchReport(sessionMeta.resultKey, (reportData: any) => {
      const detailedReport: DetailedReport = {
        data: reportData,
        stats: reportData.stats || { tests: 0, passes: 0, failures: 0, pending: 0 },
        timestamp: reportData.timestamp ? new Date(reportData.timestamp) : new Date(),
        sessionLabel: label,
        size: JSON.stringify(reportData).length
      };

      callback(detailedReport);
    });
  }

  watchAllReports(callback: (sessionLabel: string, report: DetailedReport) => void): () => void {
    const unsubscribeFunctions: (() => void)[] = [];
    
    const allSessions = this.sessionRepository.findAll();
    
    for (const sessionMeta of allSessions) {
      const unsubscribe = this.watchSessionReport(sessionMeta.label, (report) => {
        callback(sessionMeta.label, report);
      });
      unsubscribeFunctions.push(unsubscribe);
    }

    // Return function to unsubscribe from all
    return () => {
      unsubscribeFunctions.forEach(fn => fn());
    };
  }

  getReportSummary(): ReportSummary {
    const allSessions = this.sessionRepository.findAll();
    const reportSizes: Record<string, number> = {};
    
    let totalReports = 0;
    let totalTests = 0;
    let totalPasses = 0;
    let totalFailures = 0;
    let oldestReport: Date | undefined;
    let newestReport: Date | undefined;

    for (const sessionMeta of allSessions) {
      const report = this.getSessionReport(sessionMeta.label);
      if (report) {
        totalReports++;
        totalTests += report.stats.tests;
        totalPasses += report.stats.passes;
        totalFailures += report.stats.failures;
        reportSizes[sessionMeta.label] = report.size || 0;

        if (!oldestReport || report.timestamp < oldestReport) {
          oldestReport = report.timestamp;
        }
        if (!newestReport || report.timestamp > newestReport) {
          newestReport = report.timestamp;
        }
      }
    }

    return {
      totalReports,
      totalTests,
      totalPasses,
      totalFailures,
      reportSizes,
      ...(oldestReport && { oldestReport }),
      ...(newestReport && { newestReport })
    };
  }

  async exportReports(format: 'json' | 'html'): Promise<string> {
    const combinedReport = this.generateCombinedReport();
    
    if (format === 'json') {
      return JSON.stringify(combinedReport, null, 2);
    } else {
      // Basic HTML export - could be enhanced
      return `
        <html>
          <head><title>Test Report</title></head>
          <body>
            <h1>Combined Test Report</h1>
            <p>Generated: ${combinedReport.generatedAt}</p>
            <h2>Summary</h2>
            <pre>${JSON.stringify(combinedReport.summary, null, 2)}</pre>
            <h2>Sessions</h2>
            <pre>${JSON.stringify(combinedReport.sessions, null, 2)}</pre>
          </body>
        </html>
      `;
    }
  }

  clearAllReports(): void {
    const allSessions = this.sessionRepository.findAll();
    
    for (const sessionMeta of allSessions) {
      if (this.reportRepository.hasReport(sessionMeta.resultKey)) {
        this.reportRepository.clearReport(sessionMeta.resultKey);
      }
    }
  }

  validateSessionReport(sessionLabel: string): boolean {
    const report = this.getSessionReport(sessionLabel);
    return report !== null && this.reportRepository.validateReport(report.data);
  }

  getReportValidationErrors(sessionLabel: string): string[] {
    const errors: string[] = [];
    const report = this.getSessionReport(sessionLabel);
    
    if (!report) {
      errors.push('Report not found');
      return errors;
    }

    if (!report.data) {
      errors.push('Report data is missing');
    }

    if (!report.stats) {
      errors.push('Report stats are missing');
    } else {
      if (typeof report.stats.tests !== 'number') {
        errors.push('Invalid test count');
      }
      if (typeof report.stats.passes !== 'number') {
        errors.push('Invalid pass count');
      }
      if (typeof report.stats.failures !== 'number') {
        errors.push('Invalid failure count');
      }
    }

    return errors;
  }
}
