import chalk from "chalk";
import { runSourceSyncCheck, type SyncCheckResult, type CheckError } from "./lib/sync-check-source";
import { runModuleTestCaseSyncCheck, runAppTestCaseSyncCheck } from "./lib/sync-check-tests";

export type { SyncCheckResult, CheckError } from "./lib/sync-check-source";

export async function runSyncCheck(
  config: { modulesRoot?: string; appRoot?: string },
  cwd: string,
): Promise<SyncCheckResult> {
  const sourceResult = await runSourceSyncCheck(config, cwd);
  if (config.modulesRoot) {
    const testCaseErrors = await runModuleTestCaseSyncCheck(config.modulesRoot, cwd);
    sourceResult.errors.push(...testCaseErrors);
  }
  if (config.appRoot) {
    const storyErrors = await runAppTestCaseSyncCheck(config.appRoot, cwd);
    sourceResult.errors.push(...storyErrors);
  }
  sourceResult.exitCode = sourceResult.errors.length > 0 ? 1 : 0;
  return sourceResult;
}

export function formatSyncCheckReport(result: SyncCheckResult): string {
  const lines: string[] = [];
  lines.push(chalk.bold("docs-sync-check: Checking source-documentation correspondence...\n"));

  if (result.errors.length > 0) {
    lines.push(chalk.red.bold("Errors:\n"));
    const byCategory = new Map<string, CheckError[]>();
    for (const error of result.errors) {
      const existing = byCategory.get(error.category) ?? [];
      existing.push(error);
      byCategory.set(error.category, existing);
    }
    for (const [category, categoryErrors] of byCategory) {
      lines.push(chalk.cyan(`  Category: ${category}\n`));
      for (const error of categoryErrors) {
        if (error.type === "missing-doc") {
          lines.push(`  ${chalk.red(error.sourcePath)}`);
          lines.push(`    ${chalk.yellow("Missing documentation for:")} ${error.expectedBasename}`);
        } else if (error.type === "orphaned-doc") {
          lines.push(`  ${chalk.red(error.docPath)}`);
          lines.push(
            `    ${chalk.yellow("Orphaned documentation:")} no source file for ${error.expectedBasename}`,
          );
        } else if (error.type === "missing-test-case") {
          lines.push(`  ${chalk.red(error.sourcePath)}`);
          lines.push(
            `    ${chalk.yellow("Missing test case:")} "${error.expectedBasename}" is in doc but not in test`,
          );
        } else if (error.type === "extra-test-case") {
          lines.push(`  ${chalk.red(error.sourcePath)}`);
          lines.push(
            `    ${chalk.yellow("Extra test case:")} "${error.expectedBasename}" is in test but not in doc`,
          );
        } else if (error.type === "missing-test-file") {
          lines.push(`  ${chalk.red(error.docPath)}`);
          lines.push(`    ${chalk.yellow("Missing test file:")} ${error.expectedBasename}`);
        }
        lines.push("");
      }
    }
  } else {
    lines.push(chalk.green("All source files have corresponding documentation.\n"));
  }

  lines.push(chalk.bold("Summary:"));
  lines.push(`  Categories checked: ${result.summary.categoriesChecked}`);
  lines.push(
    `  Source files: ${result.summary.totalSources}, Doc files: ${result.summary.totalDocs}`,
  );

  if (result.errors.length > 0) {
    lines.push(chalk.red(`  Errors: ${result.errors.length}`));
    lines.push("");
    lines.push(chalk.red.bold(`docs-sync-check failed with ${result.errors.length} error(s).`));
  } else {
    lines.push(chalk.green("  Errors: 0"));
    lines.push("");
    lines.push(chalk.green.bold("docs-sync-check passed."));
  }

  return lines.join("\n");
}
