import { expect, test } from "bun:test";
import { diagnose, parse } from "../src/index.ts";

const source = `import type { PublicUser } from "@/types/user.ts"
import PublicLayout from "@/layouts/PublicLayout.wrn"

component ProfileCard {
  props {
    user: PublicUser
    title?: string
    open: boolean = false
    size: "small" | "medium" | "large" = "medium"
  }

  state query: string = ""
  state {
    selected: PublicUser | null = null
    loading: boolean = false
  }
  client state { menuOpen: boolean = false }
  server state { sessionId: string | null = null }

  computed ready: boolean = selected !== null
  computed { label: string = title || user.name }

  outputs {
    confirm(payload: PublicUser)
    cancel()
  }

  functions {
    client async function confirmed(payload: PublicUser): Promise<void> {
      const result = await server.confirmed(payload)
      output.confirm(result)
    }
    server async function confirmed(payload: PublicUser): Promise<PublicUser> {
      return payload
    }
    shared function normalize(value: string): string {
      return value.trim()
    }
  }

  view { <button>{label}</button> }
}`;

test("parses the WRNexusJS 0.6 language surface", () => {
  const ast = parse(source);
  expect(ast.structuredImports).toHaveLength(2);
  expect(ast.props.map((prop) => [prop.name, prop.required])).toEqual([
    ["user", true],
    ["title", false],
    ["open", false],
    ["size", false],
  ]);
  expect(ast.states.map((state) => `${state.runtime}:${state.name}`)).toEqual([
    "shared:query",
    "shared:selected",
    "shared:loading",
    "client:menuOpen",
    "server:sessionId",
  ]);
  expect(ast.outputs.map((output) => output.name)).toEqual(["confirm", "cancel"]);
  expect(ast.runtimeFunctions.map((fn) => `${fn.runtime}:${fn.name}`)).toEqual([
    "client:confirmed",
    "server:confirmed",
    "shared:normalize",
  ]);
});

test("supports global and page store roots", () => {
  const globalStore = parse(`global store UserStore {
    state { user: string | null = null }
    computed { authenticated: boolean = user !== null }
    persist { storage = "local" include = ["user"] version = 1 }
    lifecycle { serverInit {} clientInit {} hydrate {} dispose {} }
    functions { client function clear(): void { user = null } }
  }`);
  const pageStore = parse(`page store SearchStore { state { query: string = "" } }`);
  expect(globalStore.kind).toBe("global-store");
  expect(globalStore.persist?.storage).toBe("local");
  expect(globalStore.storeLifecycle).toEqual(
    expect.objectContaining({ serverInit: "", dispose: "" }),
  );
  expect(pageStore.kind).toBe("page-store");
});

test("rejects duplicate implementations in the same runtime", () => {
  const diagnostics = diagnose(`component Invalid {
    functions {
      client function save(): void {}
      client function save(): void {}
    }
    view { <div></div> }
  }`);
  expect(diagnostics.some((diagnostic) => diagnostic.code === "WRN-FUNCTION-DUPLICATE")).toBe(true);
});

test("readonly prop diagnostics ignore comparisons, strings, and shadowing parameters", () => {
  const diagnostics = diagnose(`component Valid {
    props { name: string = "field" mode: string = "exact" items: any[] = [] }
    functions {
      client function inspect(name, localItems) {
        if (mode === "exact") localItems = items.filter((item) => item.name === name)
        return document.querySelector("input[name='" + name + "']")
      }
    }
    view { <div></div> }
  }`);
  expect(diagnostics.some((diagnostic) => diagnostic.code === "WRN-PROP-READONLY")).toBe(false);
});

test("readonly prop diagnostics still reject direct prop assignments", () => {
  const diagnostics = diagnose(`component Invalid {
    props { open: boolean = false }
    functions { client function mutate() { open = true } }
    view { <div></div> }
  }`);
  expect(diagnostics.some((diagnostic) => diagnostic.code === "WRN-PROP-READONLY")).toBe(true);
});
