import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, it, expect, afterEach } from "vitest";
import { copyTemplateDir, scaffoldModuleBoilerplate } from "./scaffold";

describe("scaffoldModuleBoilerplate", () => {
  let tmpDir: string;

  afterEach(() => {
    if (tmpDir) {
      fs.rmSync(tmpDir, { recursive: true, force: true });
    }
  });

  it("creates expected directories and files", () => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "scaffold-test-"));
    const moduleDir = path.join(tmpDir, "test-module");
    fs.mkdirSync(moduleDir, { recursive: true });

    scaffoldModuleBoilerplate(moduleDir, "test-module");

    for (const dir of ["db", "command", "executor", "generated", "lib", "query", "testing"]) {
      expect(fs.existsSync(path.join(moduleDir, dir))).toBe(true);
    }

    for (const dir of ["db", "command", "executor", "query"]) {
      expect(fs.existsSync(path.join(moduleDir, dir, ".gitkeep"))).toBe(true);
    }

    const expectedFiles = [
      "module.ts",
      "index.ts",
      "permissions.ts",
      "tailor.config.ts",
      "package.json",
      "eslint.config.js",
      "tsconfig.json",
      "lib/types.ts",
      "testing/fixtures.ts",
      "generated/kysely-tailordb.ts",
    ];
    for (const file of expectedFiles) {
      expect(fs.existsSync(path.join(moduleDir, file))).toBe(true);
    }

    const permissions = fs.readFileSync(path.join(moduleDir, "permissions.ts"), "utf-8");
    expect(permissions).toContain('definePermissions("test-module"');

    const config = fs.readFileSync(path.join(moduleDir, "tailor.config.ts"), "utf-8");
    expect(config).toContain('name: "test-module"');

    const pkg = fs.readFileSync(path.join(moduleDir, "package.json"), "utf-8");
    expect(pkg).toContain('"name": "test-module"');
    expect(pkg).not.toContain("workspace:*");

    const moduleFile = fs.readFileSync(path.join(moduleDir, "module.ts"), "utf-8");
    expect(moduleFile).toContain("defineModule");
    expect(moduleFile).toContain("commands");
    expect(moduleFile).toContain("queries");
  });
});

describe("copyTemplateDir", () => {
  let tmpDir: string;

  afterEach(() => {
    if (tmpDir) {
      fs.rmSync(tmpDir, { recursive: true, force: true });
    }
  });

  it("copies files recursively with placeholder replacement", () => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "copy-template-test-"));
    const srcDir = path.join(tmpDir, "src");
    const destDir = path.join(tmpDir, "dest");

    fs.mkdirSync(path.join(srcDir, "backend"), { recursive: true });
    fs.mkdirSync(path.join(srcDir, "frontend/src"), { recursive: true });
    fs.writeFileSync(
      path.join(srcDir, "backend/package.json"),
      '{ "name": "template-app-backend", "dependencies": { "@tailor-platform/erp-kit": "workspace:*" } }\n',
    );
    fs.writeFileSync(
      path.join(srcDir, "frontend/package.json"),
      '{ "name": "template-app-frontend" }\n',
    );
    fs.writeFileSync(path.join(srcDir, "frontend/index.html"), "<title>template-app</title>\n");
    fs.writeFileSync(path.join(srcDir, "frontend/src/main.tsx"), "// app\n");

    const replacements = {
      "template-app-frontend": "my-app-frontend",
      "template-app-backend": "my-app",
      "template-app": "my-app",
      '"workspace:*"': '"1.0.0"',
    };
    const placeholderFiles = new Set(["package.json", "index.html"]);
    copyTemplateDir(srcDir, destDir, replacements, placeholderFiles);

    expect(fs.existsSync(path.join(destDir, "backend/package.json"))).toBe(true);
    expect(fs.existsSync(path.join(destDir, "frontend/package.json"))).toBe(true);
    expect(fs.existsSync(path.join(destDir, "frontend/index.html"))).toBe(true);
    expect(fs.existsSync(path.join(destDir, "frontend/src/main.tsx"))).toBe(true);

    const backendPkg = fs.readFileSync(path.join(destDir, "backend/package.json"), "utf-8");
    expect(backendPkg).toContain('"name": "my-app"');
    expect(backendPkg).not.toContain("template-app-backend");
    expect(backendPkg).toContain('"1.0.0"');
    expect(backendPkg).not.toContain("workspace:*");

    const frontendPkg = fs.readFileSync(path.join(destDir, "frontend/package.json"), "utf-8");
    expect(frontendPkg).toContain('"name": "my-app-frontend"');
    expect(frontendPkg).not.toContain("template-app-frontend");

    const indexHtml = fs.readFileSync(path.join(destDir, "frontend/index.html"), "utf-8");
    expect(indexHtml).toContain("<title>my-app</title>");
    expect(indexHtml).not.toContain("template-app");
  });

  it("renames __dot__gitignore to .gitignore", () => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "copy-template-test-"));
    const srcDir = path.join(tmpDir, "src");
    const destDir = path.join(tmpDir, "dest");

    fs.mkdirSync(srcDir, { recursive: true });
    fs.writeFileSync(path.join(srcDir, "__dot__gitignore"), "node_modules\n");

    copyTemplateDir(srcDir, destDir, {}, new Set());

    expect(fs.existsSync(path.join(destDir, ".gitignore"))).toBe(true);
    expect(fs.existsSync(path.join(destDir, "__dot__gitignore"))).toBe(false);
  });

  it("does not overwrite existing files", () => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "copy-template-test-"));
    const srcDir = path.join(tmpDir, "src");
    const destDir = path.join(tmpDir, "dest");

    fs.mkdirSync(srcDir, { recursive: true });
    fs.mkdirSync(destDir, { recursive: true });
    fs.writeFileSync(path.join(srcDir, "file.txt"), "template content");
    fs.writeFileSync(path.join(destDir, "file.txt"), "existing content");

    copyTemplateDir(srcDir, destDir, {}, new Set());

    const content = fs.readFileSync(path.join(destDir, "file.txt"), "utf-8");
    expect(content).toBe("existing content");
  });
});
