import { flushPromises, mount, type VueWrapper } from "@vue/test-utils";
import { describe, expect, it, vi } from "vitest";

import App from "./App.vue";

async function mountLoadedApp(): Promise<VueWrapper> {
  const wrapper = mount(App, { attachTo: document.body });
  await flushPromises();
  return wrapper;
}

describe("App", () => {
  it("renders the canonical demo PlayerView", async () => {
    const wrapper = await mountLoadedApp();

    expect(wrapper.text()).toContain("听娜娜讲故事");
    expect(wrapper.text()).toContain("娜娜");
    expect(wrapper.text()).toContain("雨水沿着废弃站台的铁棚落下");
    expect(wrapper.text()).toContain("你真的会在天亮前回来吗？");
    expect(wrapper.get('textarea[aria-label="自由输入"]')).toBeDefined();
    wrapper.unmount();
  });

  it("copies an editable suggestion without submitting it", async () => {
    const wrapper = await mountLoadedApp();
    const startingNode = wrapper.get(".statusline span").text();

    await wrapper.get(".suggestions button").trigger("click");

    const composer = wrapper.get<HTMLTextAreaElement>("textarea");
    expect(composer.element.value.length).toBeGreaterThan(0);
    expect(wrapper.get(".statusline span").text()).toBe(startingNode);
    expect(wrapper.find(".turn-status").text()).toBe("");
    expect(wrapper.find(".beat--action").exists()).toBe(false);
    wrapper.unmount();
  });

  it("submits a speak_or_act intent, clears the draft, and exposes a busy state", async () => {
    const wrapper = await mountLoadedApp();
    const composer = wrapper.get<HTMLTextAreaElement>('textarea[aria-label="自由输入"]');

    await composer.setValue("我答应你，天亮前一定回来。");
    await wrapper.get("form.composer").trigger("submit");

    expect(composer.element.value).toBe("");
    expect(wrapper.get(".send-button").text()).toBe("发送中…");
    expect(composer.attributes("disabled")).toBeDefined();
    expect(wrapper.get(".continue-button").attributes("disabled")).toBeDefined();

    await vi.waitFor(() => {
      expect(wrapper.text()).toContain("我答应你，天亮前一定回来。");
      expect(wrapper.get(".statusline span").text()).toBe("node_002");
    });
    expect(wrapper.get(".send-button").text()).toBe("发送");
    wrapper.unmount();
  });

  it("submits Continue through the same turn path with no player speech", async () => {
    const wrapper = await mountLoadedApp();

    await wrapper.get(".continue-button").trigger("click");
    expect(wrapper.get(".continue-button").text()).toBe("继续中…");

    await vi.waitFor(() => {
      expect(wrapper.text()).toContain("一阵风越过站台");
    });
    expect(wrapper.findAll(".beat--action")).toHaveLength(0);
    expect(wrapper.get(".statusline span").text()).toBe("node_002");
    wrapper.unmount();
  });

  it("opens mutually exclusive, PlayerView-only record and relationship panels", async () => {
    const wrapper = await mountLoadedApp();
    const [recordsButton, relationshipButton] = wrapper.findAll(".top-actions button");

    await recordsButton.trigger("click");
    expect(wrapper.findAll('[role="dialog"]')).toHaveLength(1);
    expect(wrapper.get("#records-panel").text()).toContain("旧手电筒");
    expect(wrapper.get("#records-panel").text()).toContain("停摆的站钟");
    expect(wrapper.get("#records-panel").text()).toContain("还没有被接受的许诺");

    await relationshipButton.trigger("click");
    expect(wrapper.findAll('[role="dialog"]')).toHaveLength(1);
    expect(wrapper.find("#records-panel").exists()).toBe(false);

    const relationshipText = wrapper.get("#relationship-panel").text();
    expect(relationshipText).toContain("好感");
    expect(relationshipText).toContain("渐暖");
    expect(relationshipText).not.toMatch(/\d/);
    for (const forbidden of ["roll", "target", "hidden_facts", "world_flags", "checks"]) {
      expect(wrapper.text()).not.toContain(forbidden);
    }
    wrapper.unmount();
  });

  it("selects a rewind node without claiming to persist a branch", async () => {
    const wrapper = await mountLoadedApp();
    const historyButton = wrapper.findAll(".top-actions button")[2];

    await historyButton.trigger("click");
    expect(wrapper.find(".rewind-placeholder").exists()).toBe(false);

    const nodeButton = wrapper.get(".history-list button");
    await nodeButton.trigger("click");
    expect(nodeButton.attributes("aria-pressed")).toBe("true");
    expect(wrapper.get(".rewind-placeholder").text()).toContain("从这里继续（尚未接入）");
    expect(wrapper.get(".rewind-placeholder button").attributes("disabled")).toBeDefined();
    expect(wrapper.get(".rewind-placeholder").text()).toContain("不会更改当前线路");
    wrapper.unmount();
  });

  it("closes the active panel with Escape", async () => {
    const wrapper = await mountLoadedApp();
    await wrapper.findAll(".top-actions button")[0].trigger("click");

    window.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
    await wrapper.vm.$nextTick();

    expect(wrapper.find('[role="dialog"]').exists()).toBe(false);
    wrapper.unmount();
  });
});
