import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { describe, it, expect, vi, afterEach } from "vitest";
import { runProgressLog } from "./log";
import { runProgressSchema } from "./schema-cmd";

function makeTmpApp(): string {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "progress-test-"));
  execFileSync("git", ["init"], { cwd: dir });
  execFileSync("git", ["config", "user.email", "test@test.com"], { cwd: dir });
  execFileSync("git", ["config", "user.name", "test"], { cwd: dir });
  execFileSync("git", ["commit", "--allow-empty", "-m", "init"], { cwd: dir });
  return dir;
}

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

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

  it("creates .erp-kit/progress.jsonl and appends a valid entry", () => {
    tmpDir = makeTmpApp();
    const input = JSON.stringify({
      v: 1,
      sessionId: "s1",
      prompt: "test prompt",
      event: "note",
      data: { message: "test", tags: [] },
    });

    const result = runProgressLog(input, false, tmpDir);
    expect(result.exitCode).toBe(0);

    const logFile = path.join(tmpDir, ".erp-kit", "progress.jsonl");
    expect(fs.existsSync(logFile)).toBe(true);

    const lines = fs.readFileSync(logFile, "utf-8").trim().split("\n");
    expect(lines).toHaveLength(1);

    const entry = JSON.parse(lines[0]) as Record<string, unknown>;
    expect(entry.sessionId).toBe("s1");
    expect(entry.timestamp).toBeDefined();
    expect(entry.git).toBeDefined();
    expect(entry.conversation).toEqual([]);
  });

  it("appends multiple entries", () => {
    tmpDir = makeTmpApp();
    const input1 = JSON.stringify({
      v: 1,
      sessionId: "s1",
      prompt: "test prompt",
      event: "note",
      data: { message: "first", tags: [] },
    });
    const input2 = JSON.stringify({
      v: 1,
      sessionId: "s1",
      prompt: "test prompt",
      event: "note",
      data: { message: "second", tags: [] },
    });

    runProgressLog(input1, false, tmpDir);
    runProgressLog(input2, false, tmpDir);

    const logFile = path.join(tmpDir, ".erp-kit", "progress.jsonl");
    const lines = fs.readFileSync(logFile, "utf-8").trim().split("\n");
    expect(lines).toHaveLength(2);
  });

  it("rejects invalid JSON", () => {
    tmpDir = makeTmpApp();
    const result = runProgressLog("not json", false, tmpDir);
    expect(result.exitCode).toBe(1);
  });

  it("rejects invalid schema (missing prompt)", () => {
    tmpDir = makeTmpApp();
    const input = JSON.stringify({
      v: 1,
      sessionId: "s1",
      event: "note",
      data: { message: "x", tags: [] },
    });
    const result = runProgressLog(input, false, tmpDir);
    expect(result.exitCode).toBe(1);
  });

  it("dry-run does not write to file", () => {
    tmpDir = makeTmpApp();
    const input = JSON.stringify({
      v: 1,
      sessionId: "s1",
      prompt: "test prompt",
      event: "note",
      data: { message: "test", tags: [] },
    });

    const result = runProgressLog(input, true, tmpDir);
    expect(result.exitCode).toBe(0);

    const logFile = path.join(tmpDir, ".erp-kit", "progress.jsonl");
    expect(fs.existsSync(logFile)).toBe(false);
  });
});

describe("runProgressSchema", () => {
  it("outputs valid JSON Schema", () => {
    const spy = vi.spyOn(console, "log").mockImplementation(() => undefined);

    const result = runProgressSchema();

    expect(result.exitCode).toBe(0);
    expect(spy).toHaveBeenCalledOnce();

    const schema = JSON.parse(String(spy.mock.calls[0]?.[0])) as Record<string, unknown>;
    expect(schema).toHaveProperty("anyOf");

    spy.mockRestore();
  });
});
