import crypto from "crypto";
import fs from "fs";
import os from "os";
import path from "path";
import { afterEach, describe, expect, it } from "vitest";
import {
  appendFileToHash,
  appendInputToHash,
  computeTemplateFingerprint,
} from "./fingerprint.mjs";

const tempDirs = [];

afterEach(() => {
  for (const dir of tempDirs.splice(0)) {
    fs.rmSync(dir, { recursive: true, force: true });
  }
});

function mkTempDir() {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-db-fingerprint-"));
  tempDirs.push(dir);
  return dir;
}

describe("database-fingerprint", () => {
  it("hashes files, directories, and missing paths deterministically", () => {
    const productDir = mkTempDir();
    fs.mkdirSync(path.join(productDir, "schema"), { recursive: true });
    fs.writeFileSync(path.join(productDir, "schema", "001.sql"), "select 1;\n");
    fs.mkdirSync(path.join(productDir, ".testkit", "ignored"), { recursive: true });
    fs.writeFileSync(path.join(productDir, ".testkit", "ignored", "skip.sql"), "skip");

    const hashA = crypto.createHash("sha256");
    appendInputToHash(hashA, productDir, "schema");
    appendInputToHash(hashA, productDir, "missing");
    const digestA = hashA.digest("hex");

    const hashB = crypto.createHash("sha256");
    appendInputToHash(hashB, productDir, "schema");
    appendInputToHash(hashB, productDir, "missing");
    const digestB = hashB.digest("hex");

    expect(digestA).toBe(digestB);
  });

  it("hashes file contents and missing files", () => {
    const productDir = mkTempDir();
    const filePath = path.join(productDir, "a.txt");
    fs.writeFileSync(filePath, "alpha");

    const present = crypto.createHash("sha256");
    appendFileToHash(present, productDir, filePath);
    const presentDigest = present.digest("hex");

    const missing = crypto.createHash("sha256");
    appendFileToHash(missing, productDir, path.join(productDir, "missing.txt"));
    const missingDigest = missing.digest("hex");

    expect(presentDigest).not.toBe(missingDigest);
  });

  it("computes template fingerprints from config inputs and env files", async () => {
    const productDir = mkTempDir();
    fs.mkdirSync(path.join(productDir, "schema"), { recursive: true });
    fs.writeFileSync(path.join(productDir, "schema", "001.sql"), "select 1;\n");
    fs.writeFileSync(path.join(productDir, ".env.testkit"), "TOKEN=alpha\n");

    const config = {
      productDir,
      testkit: {
        database: {
          provider: "local",
          selectedBackend: "local",
          template: {
            inputs: ["schema"],
            migrate: [
              {
                kind: "command",
                cmd: "npm run migrate",
                cwd: ".",
                inputs: [],
              },
            ],
            seed: [],
            verify: [],
          },
        },
        envFiles: [".env.testkit"],
      },
    };

    const first = await computeTemplateFingerprint(config);
    fs.writeFileSync(path.join(productDir, ".env.testkit"), "TOKEN=beta\n");
    const second = await computeTemplateFingerprint(config);

    expect(first).not.toBe(second);
  });
});
