import { afterEach, expect, test } from "bun:test";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { checkApplication, generateApplicationTypes } from "../src/types.ts";
import { inspectComponent } from "../src/inspect.ts";

const roots: string[] = [];
afterEach(() => {
  for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

function fixture(): string {
  const root = mkdtempSync(join(tmpdir(), "wrnexus-types-"));
  roots.push(root);
  for (const dir of ["pages/users", "components", "api", "realtime", "queues", "locales"])
    mkdirSync(join(root, "app", dir), { recursive: true });
  writeFileSync(join(root, ".env.example"), "PUBLIC_API_URL=https://example.test\n");
  writeFileSync(join(root, "app/pages/index.wrn"), "page Home { view { <h1>Home</h1> } }\n");
  writeFileSync(
    join(root, "app/pages/users/[id].wrn"),
    "page User { props { id: string } view { <p>{id}</p> } }\n",
  );
  writeFileSync(
    join(root, "app/components/Button.wrn"),
    "component Button { props { label: string } outputs { press(event: MouseEvent) } view { <button>{label}</button> } }\n",
  );
  writeFileSync(join(root, "app/api/users.ts"), "export default () => new Response('ok');\n");
  writeFileSync(join(root, "app/realtime/chat.ts"), "export default {};\n");
  writeFileSync(join(root, "app/queues/email.ts"), "export default {};\n");
  writeFileSync(join(root, "app/locales/en.json"), JSON.stringify({ common: { save: "Save" } }));
  return root;
}

test("generate types emits application-wide deterministic contracts", () => {
  const root = fixture();
  const result = generateApplicationTypes(root);
  const output = readFileSync(join(root, result.file), "utf8");
  expect(existsSync(join(root, "app/routes.gen.ts"))).toBe(true);
  expect(output).toContain('type RouteName = "index" | "users.id"');
  expect(output).toContain('type EnvironmentKey = "PUBLIC_API_URL"');
  expect(output).toContain('type TranslationKey = "common.save"');
  expect(output).toContain('type QueueName = "email"');
  expect(output).toContain('"Button": { props: { "label": string }');
  expect(output).toContain("interface ApiContracts");
  expect(output).toContain('"/api/users": { default: ApiContract<');
  expect(output).toContain("interface RealtimeMessages");
  expect(output).toContain('"/realtime/chat": RealtimeMessage<');
  expect(output).toContain("interface QueuePayloads");
  expect(output).toContain('"email": QueuePayload<');
  expect(output).toContain("type MiddlewareContexts = Record<string, never>");
  expect(output).toContain("type DatabaseQueries = Record<string, never>");
  expect(output).not.toMatch(/interface\s+\w+\s*\{\s*\}/);
});

test("application checker validates every wrn source", () => {
  expect(checkApplication(fixture()).filter((item) => item.category === "error")).toEqual([]);
}, 15_000);

test("component inspection exposes its typed public contract", () => {
  const value = inspectComponent(fixture(), "button") as { name: string; props: unknown[] };
  expect(value.name).toBe("Button");
  expect(value.props).toEqual([{ name: "label", type: "string", required: true }]);
});
