import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { discoverApps, collectScripts } from "./scripts";

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

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "run-test-"));
    fs.mkdirSync(path.join(tmpDir, "apps"), { recursive: true });
  });

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

  it("returns directories under apps/", () => {
    fs.mkdirSync(path.join(tmpDir, "apps", "ims"));
    fs.mkdirSync(path.join(tmpDir, "apps", "crm"));
    const apps = discoverApps(tmpDir);
    expect(apps.sort()).toEqual(["crm", "ims"]);
  });

  it("ignores files in apps/", () => {
    fs.mkdirSync(path.join(tmpDir, "apps", "ims"));
    fs.writeFileSync(path.join(tmpDir, "apps", ".gitkeep"), "");
    const apps = discoverApps(tmpDir);
    expect(apps).toEqual(["ims"]);
  });

  it("returns empty array when apps/ does not exist", () => {
    fs.rmSync(path.join(tmpDir, "apps"), { recursive: true });
    const apps = discoverApps(tmpDir);
    expect(apps).toEqual([]);
  });
});

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

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "run-test-"));
    fs.mkdirSync(path.join(tmpDir, "apps", "ims"), { recursive: true });
  });

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

  it("collects scripts from app root package.json", () => {
    fs.writeFileSync(
      path.join(tmpDir, "apps", "ims", "package.json"),
      JSON.stringify({ scripts: { deploy: "tailor-sdk apply", seed: "node seed.mjs" } }),
    );
    const scripts = collectScripts(tmpDir, "ims");
    expect(scripts).toEqual([
      { name: "deploy", label: "deploy", cwd: path.join(tmpDir, "apps", "ims") },
      { name: "seed", label: "seed", cwd: path.join(tmpDir, "apps", "ims") },
    ]);
  });

  it("collects scripts from subdirectories with prefix", () => {
    const backend = path.join(tmpDir, "apps", "ims", "backend");
    fs.mkdirSync(backend, { recursive: true });
    fs.writeFileSync(
      path.join(backend, "package.json"),
      JSON.stringify({ scripts: { deploy: "tailor-sdk apply", seed: "node seed.mjs" } }),
    );
    const scripts = collectScripts(tmpDir, "ims");
    expect(scripts).toEqual([
      { name: "deploy", label: "backend > deploy", cwd: backend },
      { name: "seed", label: "backend > seed", cwd: backend },
    ]);
  });

  it("merges root and subdirectory scripts", () => {
    fs.writeFileSync(
      path.join(tmpDir, "apps", "ims", "package.json"),
      JSON.stringify({ scripts: { lint: "oxlint ." } }),
    );
    const backend = path.join(tmpDir, "apps", "ims", "backend");
    fs.mkdirSync(backend, { recursive: true });
    fs.writeFileSync(
      path.join(backend, "package.json"),
      JSON.stringify({ scripts: { deploy: "tailor-sdk apply" } }),
    );
    const scripts = collectScripts(tmpDir, "ims");
    expect(scripts).toEqual([
      { name: "lint", label: "lint", cwd: path.join(tmpDir, "apps", "ims") },
      { name: "deploy", label: "backend > deploy", cwd: backend },
    ]);
  });

  it("returns empty array when no package.json found", () => {
    const scripts = collectScripts(tmpDir, "ims");
    expect(scripts).toEqual([]);
  });

  it("skips a malformed package.json instead of throwing", () => {
    fs.writeFileSync(path.join(tmpDir, "apps", "ims", "package.json"), "{ not json");
    const backend = path.join(tmpDir, "apps", "ims", "backend");
    fs.mkdirSync(backend, { recursive: true });
    fs.writeFileSync(
      path.join(backend, "package.json"),
      JSON.stringify({ scripts: { deploy: "tailor-sdk apply" } }),
    );
    const scripts = collectScripts(tmpDir, "ims");
    expect(scripts).toEqual([{ name: "deploy", label: "backend > deploy", cwd: backend }]);
  });

  it("skips node_modules directories", () => {
    const nm = path.join(tmpDir, "apps", "ims", "node_modules", "foo");
    fs.mkdirSync(nm, { recursive: true });
    fs.writeFileSync(path.join(nm, "package.json"), JSON.stringify({ scripts: { test: "jest" } }));
    const scripts = collectScripts(tmpDir, "ims");
    expect(scripts).toEqual([]);
  });
});
