import { expect, test } from "bun:test";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { checkWrnSource } from "../src/index.ts";

function app(): string {
  const root = mkdtempSync(join(tmpdir(), "wrn-typecheck-"));
  mkdirSync(join(root, "app", "types"), { recursive: true });
  writeFileSync(
    join(root, "app", "types", "user.ts"),
    "export interface User { id: string; name: string }\n",
  );
  writeFileSync(
    join(root, "app", "types", "global.d.ts"),
    "declare interface RequestError { message: string }\n",
  );
  return root;
}

test("loads app types and maps output payload errors back to WRN source", () => {
  const root = app();
  const source = `import type { User } from "@/types/user.ts"
component Demo {
  props { user: User }
  outputs { confirm(payload: User) }
  functions {
    client function save(value: User): void {
      output.confirm({ id: 1, name: value.name })
    }
  }
  view { <button></button> }
}`;
  const diagnostics = checkWrnSource(source, {
    appRoot: root,
    filePath: join(root, "app", "components", "Demo.wrn"),
  });
  expect(diagnostics.some((diagnostic) => diagnostic.code === "WRN-TYPE-2322")).toBe(true);
  expect(diagnostics.find((diagnostic) => diagnostic.code === "WRN-TYPE-2322")?.line).toBe(7);
});

test("allows the same function name in client and server runtimes", () => {
  const root = app();
  const diagnostics = checkWrnSource(
    `component Demo {
    functions {
      client async function save(value: string): Promise<void> { await server.save(value) }
      server async function save(value: string): Promise<void> {}
    }
    view { <div></div> }
  }`,
    { appRoot: root, filePath: join(root, "app", "components", "Demo.wrn") },
  );
  expect(
    diagnostics.filter((diagnostic) => diagnostic.code === "WRN-FUNCTION-DUPLICATE"),
  ).toHaveLength(0);
});

test("treats omitted runtime parameter types as dynamic JavaScript values", () => {
  const root = app();
  const diagnostics = checkWrnSource(
    `component Demo {
      outputs { change(payload: { value?: string; sourceEvent?: Event }) }
      functions {
        client function forward(type, payload) {
          const detail = payload || {}
          output[type]({ value: detail.value || "", sourceEvent: detail.sourceEvent })
        }
      }
      view { <button @click="forward('change', payload)"></button> }
    }`,
    { appRoot: root, filePath: join(root, "app", "components", "Demo.wrn") },
  );
  expect(diagnostics.filter((diagnostic) => diagnostic.code.startsWith("WRN-TYPE-"))).toEqual([]);
});

test("provides the framework context to page state expressions", () => {
  const root = app();
  const diagnostics = checkWrnSource(
    `page AccountPage {
      state user = ctx.user
      view { <main></main> }
    }`,
    { appRoot: root, filePath: join(root, "app", "pages", "account.wrn") },
  );
  expect(diagnostics.some((diagnostic) => diagnostic.code === "WRN-TYPE-2304")).toBe(false);
});
