"use strict";

const fs = require("fs");
const path = require("path");
const crypto = require("crypto");

const repoRoot = path.resolve(__dirname, "..");
const canonicalDir = path.resolve(repoRoot, "packages", "db", "migrations");
const legacyDir = path.resolve(repoRoot, "backend", "migrations");
const defaultReportPath = path.resolve(legacyDir, "stale-sql-report.json");

function parseArgs(argv) {
  const args = new Set(argv);
  return {
    write: args.has("--write"),
    failOnStale: args.has("--fail-on-stale"),
    help: args.has("--help"),
  };
}

function ensureDirectoryExists(dirPath, label) {
  if (!fs.existsSync(dirPath)) {
    throw new Error(`${label} directory not found: ${dirPath}`);
  }
}

function sha256File(filePath) {
  const hash = crypto.createHash("sha256");
  hash.update(fs.readFileSync(filePath));
  return hash.digest("hex");
}

function listFiles(dirPath) {
  return fs
    .readdirSync(dirPath)
    .filter((name) => fs.statSync(path.join(dirPath, name)).isFile())
    .sort((a, b) => a.localeCompare(b));
}

function listSqlFiles(dirPath) {
  return listFiles(dirPath).filter((name) => name.toLowerCase().endsWith(".sql"));
}

function mapByNameWithHash(dirPath, names) {
  const map = new Map();
  for (const name of names) {
    map.set(name, {
      name,
      path: path.join(dirPath, name),
      sha256: sha256File(path.join(dirPath, name)),
    });
  }
  return map;
}

function buildReport() {
  ensureDirectoryExists(canonicalDir, "Canonical migrations");
  ensureDirectoryExists(legacyDir, "Legacy migrations");

  const canonicalSql = listSqlFiles(canonicalDir);
  const legacySql = listSqlFiles(legacyDir);
  const legacyNonSql = listFiles(legacyDir).filter(
    (name) => !name.toLowerCase().endsWith(".sql")
  );

  const canonicalMap = mapByNameWithHash(canonicalDir, canonicalSql);
  const legacyMap = mapByNameWithHash(legacyDir, legacySql);

  const staleFiles = [];
  for (const legacyName of legacySql) {
    const legacyFile = legacyMap.get(legacyName);
    const canonicalFile = canonicalMap.get(legacyName);

    if (!canonicalFile) {
      staleFiles.push({
        filename: legacyName,
        status: "STALE_ONLY_IN_BACKEND",
        backend_sha256: legacyFile.sha256,
      });
      continue;
    }

    if (legacyFile.sha256 === canonicalFile.sha256) {
      staleFiles.push({
        filename: legacyName,
        status: "STALE_DUPLICATE_OF_CANONICAL",
        backend_sha256: legacyFile.sha256,
        canonical_sha256: canonicalFile.sha256,
      });
      continue;
    }

    staleFiles.push({
      filename: legacyName,
      status: "STALE_DIVERGED_FROM_CANONICAL",
      backend_sha256: legacyFile.sha256,
      canonical_sha256: canonicalFile.sha256,
    });
  }

  const canonicalOnly = canonicalSql
    .filter((name) => !legacyMap.has(name))
    .map((name) => ({
      filename: name,
      status: "CANONICAL_ONLY",
      canonical_sha256: canonicalMap.get(name).sha256,
    }));

  return {
    generated_at: new Date().toISOString(),
    canonical_dir: path.relative(repoRoot, canonicalDir),
    legacy_dir: path.relative(repoRoot, legacyDir),
    stale_sql_files: staleFiles,
    canonical_only_sql_files: canonicalOnly,
    legacy_non_sql_files: legacyNonSql,
    summary: {
      stale_total: staleFiles.length,
      stale_only_in_backend_total: staleFiles.filter(
        (f) => f.status === "STALE_ONLY_IN_BACKEND"
      ).length,
      stale_duplicate_total: staleFiles.filter(
        (f) => f.status === "STALE_DUPLICATE_OF_CANONICAL"
      ).length,
      stale_diverged_total: staleFiles.filter(
        (f) => f.status === "STALE_DIVERGED_FROM_CANONICAL"
      ).length,
      canonical_only_total: canonicalOnly.length,
    },
  };
}

function printReport(report) {
  console.log("Stale SQL Tracker");
  console.log(`- Canonical: ${report.canonical_dir}`);
  console.log(`- Legacy: ${report.legacy_dir}`);
  console.log(`- Generated: ${report.generated_at}`);
  console.log("");

  console.log(`Stale SQL files in legacy dir: ${report.summary.stale_total}`);
  for (const stale of report.stale_sql_files) {
    console.log(`  - ${stale.filename} :: ${stale.status}`);
  }

  console.log("");
  console.log(`Canonical-only SQL files: ${report.summary.canonical_only_total}`);
  for (const canonicalOnly of report.canonical_only_sql_files) {
    console.log(`  - ${canonicalOnly.filename}`);
  }

  console.log("");
  console.log(`Legacy non-SQL files: ${report.legacy_non_sql_files.length}`);
  for (const nonSql of report.legacy_non_sql_files) {
    console.log(`  - ${nonSql}`);
  }
}

function writeReport(report) {
  fs.writeFileSync(defaultReportPath, JSON.stringify(report, null, 2) + "\n", "utf8");
  console.log("");
  console.log(`Wrote stale SQL report: ${path.relative(repoRoot, defaultReportPath)}`);
}

function main() {
  const options = parseArgs(process.argv.slice(2));
  if (options.help) {
    console.log("Usage: node scripts/db-stale-sql-tracker.js [--write] [--fail-on-stale]");
    process.exit(0);
  }

  const report = buildReport();
  printReport(report);

  if (options.write) {
    writeReport(report);
  }

  if (options.failOnStale && report.summary.stale_total > 0) {
    process.exit(1);
  }
}

try {
  main();
} catch (error) {
  console.error(error.message);
  process.exit(1);
}
