import { afterAll, afterEach, beforeAll, beforeEach, expect, test } from "bun:test";
import { Window } from "happy-dom";
import { act, useState } from "react";
import type { Root } from "react-dom/client";
import { HabitForm } from "./HabitForm";
import { HabitColorPicker } from "./design-system/HabitColorPicker";

const dom = new Window({ url: "http://localhost:3000/design-system" });
const originalGlobals = new Map<string, PropertyDescriptor | undefined>();
let createRoot: typeof import("react-dom/client").createRoot;
let root: Root;
let container: HTMLDivElement;

beforeAll(async () => {
  for (const key of ["window", "document", "navigator", "HTMLElement", "HTMLInputElement", "Element", "Node", "Event", "MouseEvent", "IS_REACT_ACT_ENVIRONMENT"]) {
    originalGlobals.set(key, Object.getOwnPropertyDescriptor(globalThis, key));
    Object.defineProperty(globalThis, key, {
      configurable: true,
      writable: true,
      value: key === "window" ? dom : key === "IS_REACT_ACT_ENVIRONMENT" ? true : (dom as unknown as Record<string, unknown>)[key],
    });
  }
  ({ createRoot } = await import("react-dom/client"));
});

beforeEach(() => {
  container = document.createElement("div");
  document.body.append(container);
  root = createRoot(container);
});

afterEach(async () => {
  await act(async () => root.unmount());
  container.remove();
});

afterAll(() => {
  dom.happyDOM.abort();
  for (const [key, descriptor] of originalGlobals) {
    if (descriptor) Object.defineProperty(globalThis, key, descriptor);
    else Reflect.deleteProperty(globalThis, key);
  }
});

function button(text: string) {
  return [...container.querySelectorAll<HTMLButtonElement>("button")].find((element) => element.textContent?.trim() === text)!;
}

test("habit starters remain editable and custom creation resets the draft", async () => {
  await act(async () => root.render(<HabitForm date="2026-09-05" onCancel={() => {}} onCreated={() => {}} onExpired={() => {}} />));
  await act(async () => button("Reading").click());
  expect(container.querySelector<HTMLInputElement>('input[placeholder="e.g. Read ten pages"]')?.value).toBe("Read");
  expect(container.querySelector<HTMLSelectElement>("select")?.value).toBe("count");
  expect(container.querySelector<HTMLInputElement>('input[type="number"]')?.value).toBe("10");
  await act(async () => button("Evening routine").click());
  expect(container.querySelector<HTMLSelectElement>("select")?.value).toBe("tasks");
  expect([...container.querySelectorAll("input")].map(input => input.value)).toContain("Prepare for tomorrow");
  await act(async () => button("Start from scratch").click());
  expect(container.querySelector<HTMLInputElement>('input[placeholder="e.g. Read ten pages"]')?.value).toBe("");
  expect(container.querySelector<HTMLSelectElement>("select")?.value).toBe("manual");
});

test("compact palette discloses full controls and preserves custom color", async () => {
  function Picker() {
    const [color, setColor] = useState("#123456");
    return <HabitColorPicker value={color} onChange={setColor} compact />;
  }
  await act(async () => root.render(<Picker />));
  expect(container.querySelector('[aria-label="Suggested colors"]')?.querySelectorAll("button").length).toBe(8);
  const disclosure = button("More colors");
  const details = document.getElementById(disclosure.getAttribute("aria-controls")!)!;
  expect(details.hidden).toBe(true);
  await act(async () => disclosure.click());
  expect(details.hidden).toBe(false);
  expect(container.querySelector<HTMLInputElement>('input[type="text"]')?.value).toBe("#123456");
  await act(async () => button("Fewer colors").click());
  expect(container.querySelector('[aria-label="Progress shades using #123456"]')).not.toBeNull();
});
