import { describe, expect, test } from "bun:test";
import {
  createSourceRange,
  diagnose,
  diagnosticSummary,
  sliceSource,
  supportsSyntaxFeature,
} from "../src/index.ts";

describe("syntax contract and security diagnostics", () => {
  test("validates source ranges and summarizes diagnostic codes", () => {
    const range = createSourceRange(2, 6);
    expect(sliceSource("0123456789", range)).toBe("2345");
    expect(() => createSourceRange(-1, 2)).toThrow(RangeError);
    expect(() => createSourceRange(4, 3)).toThrow(RangeError);
    expect(supportsSyntaxFeature("runtime-markers")).toBe(true);
    expect(supportsSyntaxFeature("dynamic-eval")).toBe(false);

    const summary = diagnosticSummary([
      { code: "A", severity: "error", message: "a" },
      { code: "A", severity: "warning", message: "b" },
      { code: "B", severity: "info", message: "c" },
    ]);
    expect(summary).toEqual({ errors: 1, warnings: 1, info: 1, codes: { A: 2, B: 1 } });
  });

  test("rejects browser secret reads, executable sinks, and sensitive persistence", () => {
    const diagnostics = diagnose(`page store Unsafe {
  client state { apiToken: string = process.env.API_TOKEN }
  persist { storage = "local" include = ["apiToken"] version = 1 }
  functions {
    client function render(raw: string): void { document.write(raw); setTimeout("run()", 1) }
  }
}`);
    const codes = diagnostics.map((diagnostic) => diagnostic.code);
    expect(codes).toContain("WRN-SEC-SERVER-SECRET-SOURCE");
    expect(codes).toContain("WRN-SEC-DOM-SINK");
    expect(codes).toContain("WRN-SEC-STRING-TIMER");
    expect(codes).toContain("WRN-PERSIST-SENSITIVE");
  });

  test.each([
    [
      "server output call",
      `component Bad { outputs { save() } functions { server function run(): void { output.save() } } view { <div></div> } }`,
      "WRN-OUTPUT-SERVER-CALL",
    ],
    [
      "unknown output call",
      `component Bad { functions { client function run(): void { output.missing() } } view { <button @click="run()">Run</button> } }`,
      "WRN-OUTPUT-UNKNOWN",
    ],
    [
      "server API in client function",
      `component Bad { functions { client function run(): void { process.cwd() } } view { <button @click="run()">Run</button> } }`,
      "WRN-CLIENT-SERVER-API",
    ],
    [
      "browser API in server function",
      `component Bad { functions { server function run(): void { document.title = "bad" } } view { <div></div> } }`,
      "WRN-SERVER-BROWSER-API",
    ],
    [
      "non-serializable shared state",
      `global store Bad { state { values: unknown = new Map() } }`,
      "WRN-STATE-NON-SERIALIZABLE",
    ],
    [
      "unknown persisted state",
      `global store Bad { state { value: string = "x" } persist { storage = "local" include = ["missing"] version = 1 } }`,
      "WRN-PERSIST-UNKNOWN-FIELD",
    ],
  ])("diagnoses %s", (_name, source, expectedCode) => {
    expect(diagnose(source).map((diagnostic) => diagnostic.code)).toContain(expectedCode);
  });
});
