import path from "path";

export function buildStatusArtifact({
  productDir,
  results,
  typeValues,
  suiteSelectors,
  fileNames,
  shard,
  serviceFilter,
  metadata,
}) {
  const executedResults = results.filter((result) => !result.skipped);
  const effectivelySkippedResults = executedResults.filter(isEffectivelySkippedService);
  const tests = [];

  for (const result of executedResults) {
    for (const suite of result.suites) {
      for (const file of suite.files) {
        const test = {
          service: result.name,
          type: suite.type,
          path: file.path,
          status: file.status,
        };
        if (file.error) {
          test.error = file.error;
        }
        if (file.reason) {
          test.reason = file.reason;
        }
        if (Array.isArray(file.failureDetails) && file.failureDetails.length > 0) {
          test.failureDetails = file.failureDetails;
        }
        tests.push(test);
      }
    }
  }

  tests.sort(
    (left, right) =>
      left.service.localeCompare(right.service) ||
      left.type.localeCompare(right.type) ||
      left.path.localeCompare(right.path)
  );

  const summary = {
    services: {
      total: executedResults.length,
      passed: executedResults.filter(
        (result) => !result.failed && !isEffectivelySkippedService(result)
      ).length,
      failed: executedResults.filter((result) => result.failed).length,
      skipped: effectivelySkippedResults.length,
    },
    tests: {
      total: tests.length,
      passed: tests.filter((test) => test.status === "passed").length,
      failed: tests.filter((test) => test.status === "failed").length,
      skipped: tests.filter((test) => test.status === "skipped").length,
      notRun: tests.filter((test) => test.status === "not_run").length,
    },
  };

  const scope = {
    types: [...(typeValues || ["all"])].sort(),
    suiteSelectors: [...(suiteSelectors || [])].map((selector) => selector.raw).sort(),
    fileNames: [...(fileNames || [])].sort(),
    shard: shard || null,
    serviceFilter: serviceFilter || null,
  };
  scope.isFullRun =
    scope.types.length === 1 &&
    scope.types[0] === "all" &&
    scope.suiteSelectors.length === 0 &&
    scope.fileNames.length === 0 &&
    scope.shard === null &&
    scope.serviceFilter === null;

  return {
    schemaVersion: 6,
    source: "testkit",
    notice: "Generated file. Do not edit manually.",
    product: {
      name: path.basename(productDir),
    },
    git: {
      branch: metadata.git?.branch || null,
      commitSha: metadata.git?.commitSha || null,
    },
    testkitVersion: metadata.testkitVersion,
    scope,
    summary,
    tests,
  };
}

export function buildRunArtifact({
  productDir,
  results,
  startedAt,
  finishedAt,
  execution,
  workerCount,
  runtimeInstanceCount,
  runtimeStats,
  typeValues,
  suiteSelectors,
  fileNames,
  shard,
  serviceFilter,
  metadata,
  summarizeDbBackend,
}) {
  const executed = results.filter((result) => !result.skipped);
  const effectivelySkippedServices = executed.filter(isEffectivelySkippedService);
  const failedServices = executed.filter((result) => result.failed);
  const totalSuites = executed.reduce((sum, result) => sum + result.suiteCount, 0);
  const completedSuites = executed.reduce((sum, result) => sum + result.completedSuiteCount, 0);
  const skippedSuites = executed.reduce((sum, result) => sum + (result.skippedSuiteCount || 0), 0);
  const failedSuites = executed.reduce((sum, result) => sum + result.failedSuiteCount, 0);
  const totalFiles = executed.reduce((sum, result) => sum + (result.totalFileCount || 0), 0);
  const passedFiles = executed.reduce((sum, result) => sum + (result.passedFileCount || 0), 0);
  const failedFiles = executed.reduce((sum, result) => sum + (result.failedFileCount || 0), 0);
  const skippedFiles = executed.reduce((sum, result) => sum + (result.skippedFileCount || 0), 0);
  const notRunFiles = executed.reduce((sum, result) => sum + (result.notRunFileCount || 0), 0);
  const dbBackend = summarizeDbBackend(results);

  return {
    schemaVersion: 6,
    source: "testkit",
    generatedAt: new Date(finishedAt).toISOString(),
    product: {
      name: path.basename(productDir),
      directory: productDir,
    },
    git: metadata.git,
    host: metadata.host,
    run: {
      status: failedServices.length > 0 ? "failed" : "passed",
      startedAt: new Date(startedAt).toISOString(),
      finishedAt: new Date(finishedAt).toISOString(),
      durationMs: finishedAt - startedAt,
      workers: execution.workers,
      fileTimeoutSeconds: execution.fileTimeoutSeconds,
      workerCount,
      runtimeInstanceCount,
      runtimeStats: runtimeStats || [],
      dbBackend,
      types: typeValues,
      suiteSelectors: suiteSelectors.map((selector) => selector.raw),
      fileNames,
      shard,
      serviceFilter,
      testkitVersion: metadata.testkitVersion,
    },
    summary: {
      services: {
        total: executed.length,
        passed: executed.length - failedServices.length - effectivelySkippedServices.length,
        failed: failedServices.length,
        skipped: effectivelySkippedServices.length,
      },
      suites: {
        total: totalSuites,
        completed: completedSuites,
        passed: completedSuites - failedSuites - skippedSuites,
        failed: failedSuites,
        skipped: skippedSuites,
      },
      files: {
        total: totalFiles,
        passed: passedFiles,
        failed: failedFiles,
        skipped: skippedFiles,
        notRun: notRunFiles,
      },
    },
    services: results.map((result) => ({
      name: result.name,
      failed: result.failed,
      skipped: result.skipped,
      suiteCount: result.suiteCount,
      completedSuiteCount: result.completedSuiteCount,
      skippedSuiteCount: result.skippedSuiteCount,
      failedSuiteCount: result.failedSuiteCount,
      totalFileCount: result.totalFileCount,
      completedFileCount: result.completedFileCount,
      passedFileCount: result.passedFileCount,
      failedFileCount: result.failedFileCount,
      skippedFileCount: result.skippedFileCount,
      notRunFileCount: result.notRunFileCount,
      durationMs: result.durationMs,
      totalTaskDurationMs: result.totalTaskDurationMs,
      dbBackend: result.dbBackend,
      suites: result.suites,
      errors: result.errors,
    })),
  };
}

function isEffectivelySkippedService(result) {
  return (
    !result.skipped &&
    !result.failed &&
    (result.skippedSuiteCount || 0) > 0 &&
    (result.skippedSuiteCount || 0) === result.suiteCount &&
    (result.notRunFileCount || 0) === 0
  );
}
