/**
 * Copyright IBM Corp. 2024, 2025
 */
import { LogWrapper } from '../../service/log-wrapper.js';
import {
  TestExecutionResult,
  RunFilteredSummary as TestRunFilteredSummary,
  ExecutionFilteredResult,
  AssertionSummary,
} from '../../models/interface.js';
import {
  filterSensitiveData,
  generateCSV,
  generatePDF,
} from '../../helpers/helper.js';

export class TestExecutionReport {
  private getResponse = (execution: TestExecutionResult) => {
    if (execution.response.stream) {
      return execution.response.stream.toString();
    } else if (execution.response.data) {
      return execution.response.data;
    } else {
      return 'Response unavailable';
    }
  };

  private formatExecution = (
    execution: TestExecutionResult,
  ): ExecutionFilteredResult | null => {
    try {
      return {
        id: execution.id,
        name: execution.itemName,
        url: execution.request?.endpoint,
        method: execution.request?.method,
        header: execution.request?.headers,
        time: execution.completedAt - execution.startedAt || 0,
        responseCode: {
          code: execution.response?.status || 408,
          name: execution.response?.statusText || 'Request Timed out',
          time: execution.response?.responseTime || 0,
          size: execution.response?.responseSize || 0,
        },
        response: this.getResponse(execution),
        responseHeaders: execution.response.headers || null,
        allTests: execution.assertions?.map(
          ({
            error,
            actualValue,
            expectedValue,
            assertion,
            action,
            key,
            metadata,
            skipped,
          }) => ({
            [assertion]: error
              ? {
                  status: false,
                  skipped,
                  error,
                  actualValue,
                  expectedValue,
                  action,
                  key,
                  metadata,
                }
              : {
                  status: true,
                  skipped,
                  actualValue,
                  expectedValue,
                  action,
                  key,
                  metadata,
                },
          }),
        ),
      };
    } catch (error) {
      LogWrapper.logError(
        '0013',
        `formatting execution with id ${execution.id}`,
        (error as Error).message,
      );
      return null;
    }
  };

  private getExecutionResults(
    executions: TestExecutionResult[],
  ): ExecutionFilteredResult[] {
    try {
      LogWrapper.logDebug('0003', 'Formatting execution results.');
      return executions
        .map(this.formatExecution)
        .filter((result) => result !== null);
    } catch (e) {
      LogWrapper.logError(
        '0013',
        'processing executions',
        (e as Error).message,
      );
      return [];
    }
  }

  private createFilteredSummary(
    collectionId: string,
    collectionName: string,
    assertionSummary: AssertionSummary[],
    results: ExecutionFilteredResult[],
    startedAt: number,
    completedAt: number,
    metadata?: { name: string; version: string; namespace: string },
  ): TestRunFilteredSummary {
    LogWrapper.logDebug(
      '0003',
      'Creating filtered summary from execution results.',
    );

    const totalAssertions = assertionSummary.reduce(
      (count, item) => count + (item.assertions?.length || 0),
      0,
    );
    const totalFailedAssertions = assertionSummary.reduce((count, item) => {
      return (
        count +
        item.assertions.reduce((innerCount, assertion) => {
          return innerCount + (assertion.error ? 1 : 0);
        }, 0)
      );
    }, 0);

    const summary: TestRunFilteredSummary = {
      id: collectionId,
      name: `${collectionName} Collection`,
      timestamp: completedAt,
      envMetadata: metadata ?? undefined,
      totalPass: totalAssertions - totalFailedAssertions,
      status: 'finished', // TODO Need to confirm the criteria for marking test as failed
      startedAt: startedAt,
      totalFail: totalFailedAssertions,
      totalTime: completedAt - startedAt,
      results: results,
    };
    return summary;
  }

  public collectReport(
    collectionId: string,
    collectionName: string,
    assertionSummary: AssertionSummary[],
    executions: TestExecutionResult[],
    startedAt: number,
    completedAt: number,
    metadata?: { name: string; version: string; namespace: string },
  ): TestRunFilteredSummary {
    LogWrapper.logInfo('0215', `${collectionName}`);

    const results = this.getExecutionResults(executions);
    const filteredSummary = this.createFilteredSummary(
      collectionId,
      collectionName,
      assertionSummary,
      results,
      startedAt,
      completedAt,
      metadata,
    );

    return filteredSummary;
  }

  public getReport(summary: TestRunFilteredSummary[], format: 'PDF' | 'CSV') {
    // Any environment variables, tokens, or request/response fields marked
    // as secret, sensitive, or under a specified key pattern (e.g., {}SECRET{}, password, token, etc.)
    // shall not be included in any exported report.
    const cleanSummary = summary.map((item) => filterSensitiveData(item));

    // The report shall be exportable in PDF and CSV formats.
    if (format === 'CSV') {
      return generateCSV(cleanSummary);
    } else {
      return generatePDF(cleanSummary);
    }
  }
}
