import { describe, expect, it } from "vitest";

if (!(globalThis as any).document) {
  const { Window } = await import("happy-dom");
  const window = new Window();
  (globalThis as any).window = window;
  (globalThis as any).document = window.document;
  Object.defineProperty(globalThis, "navigator", {
    configurable: true,
    value: window.navigator,
  });
  (globalThis as any).Node = window.Node;
  (globalThis as any).Element = window.Element;
  (globalThis as any).HTMLElement = window.HTMLElement;
  (globalThis as any).SVGElement = window.SVGElement;
  (globalThis as any).Event = window.Event;
  (globalThis as any).CustomEvent = window.CustomEvent;
  (globalThis as any).MouseEvent = window.MouseEvent;
  (globalThis as any).KeyboardEvent = window.KeyboardEvent;
  (globalThis as any).getComputedStyle = window.getComputedStyle.bind(window);
}

(globalThis as any).getSelection = () => ({ toString: () => "" });

globalThis.requestAnimationFrame = ((cb: FrameRequestCallback) => {
  cb(0);
  return 1;
}) as typeof globalThis.requestAnimationFrame;
globalThis.cancelAnimationFrame = (() => {}) as typeof globalThis.cancelAnimationFrame;

const { defineComponent, h, nextTick, ref } = await import("vue");
const { createTerminalApp } = await import("@simon_he/vue-tui");
const { DimSdkCustomProviderModelsDialog } = await import("../src/dimsdk/ui/components/CustomProviderModelsDialog.ts");

async function flushApp(app: {
  scheduler: { flush: () => void };
}) {
  await nextTick();
  await nextTick();
  app.scheduler.flush();
}

function snapshotText(app: {
  terminal: { snapshot: () => { lines: string[] } };
}): string {
  return app.terminal.snapshot().lines.join("\n");
}

async function pressKey(app: {
  events: { dispatch: (event: unknown) => boolean | void };
  scheduler: { flush: () => void };
}, key: string, options: Readonly<{ shiftKey?: boolean; code?: string }> = {}) {
  app.events.dispatch({ type: "keydown", key, code: options.code ?? key, ...options } as const);
  await flushApp(app);
}

describe("custom provider models dialog keyboard", () => {
  it("shows separator hints and supports Shift+Enter for new lines", async () => {
    const applied: string[] = [];
    const show = ref(true);
    const modelsDraft = ref("");

    const App = defineComponent({
      name: "CustomProviderModelsDialogKeyboardTestApp",
      setup() {
        return () =>
          h(DimSdkCustomProviderModelsDialog as any, {
            modelValue: show.value,
            "onUpdate:modelValue": (value: boolean) => (show.value = value),
            w: 76,
            h: 16,
            modelsDraft: modelsDraft.value,
            "onUpdate:modelsDraft": (value: string) => (modelsDraft.value = value),
            fetching: false,
            onFetch: () => {},
            onApply: () => {
              applied.push(modelsDraft.value);
              show.value = false;
            },
            onCancel: () => {
              show.value = false;
            },
          });
      },
    });

    const app = createTerminalApp({
      cols: 100,
      rows: 28,
      component: App as never,
      defaultStyle: { fg: "whiteBright", bg: "black" },
    });

    try {
      app.mount();
      await flushApp(app);

      const initial = snapshotText(app);
      expect(initial).toContain("Custom provider models");
      expect(initial).toContain(", / newline");
      expect(initial).toContain("Shift+Enter newline");

      for (const ch of "model-a") {
        await pressKey(app, ch);
      }

      await pressKey(app, "Enter", { shiftKey: true, code: "Enter" });
      expect(modelsDraft.value).toBe("model-a\n");
      expect(snapshotText(app)).toContain("Custom provider models");

      for (const ch of "model-b") {
        await pressKey(app, ch);
      }

      expect(modelsDraft.value).toBe("model-a\nmodel-b");

      await pressKey(app, "Enter", { code: "Enter" });

      expect(applied).toEqual(["model-a\nmodel-b"]);
      expect(show.value).toBe(false);
    } finally {
      app.dispose();
    }
  });
});
