// @vitest-environment jsdom

import { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { ProjectCard } from "./ProjectCard";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;

describe("ProjectCard", () => {
  let container: HTMLDivElement;
  let root: ReturnType<typeof createRoot> | null = null;

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

  afterEach(() => {
    if (root) {
      act(() => {
        root!.unmount();
      });
      root = null;
    }
    if (container.parentNode) container.remove();
  });

  function render(props: Parameters<typeof ProjectCard>[0]) {
    root = createRoot(container);
    act(() => {
      root!.render(<ProjectCard {...props} />);
    });
    return {
      get title() {
        return container.querySelector<HTMLElement>('[data-testid="project-card-title"]');
      },
      get hero() {
        return container.querySelector<HTMLElement>('[data-testid="project-card-hero"]');
      },
      get subline() {
        return container.querySelector<HTMLElement>('[data-testid="project-card-subline"]');
      },
      get footer() {
        return container.querySelector<HTMLElement>('[data-testid="project-card-footer"]');
      },
      get fill() {
        return container.querySelector<HTMLElement>('[data-testid="project-card-progress-fill"]');
      },
      get dot() {
        return container.querySelector<HTMLElement>('[data-testid="project-card-status-dot"]');
      },
      get button() {
        return container.querySelector<HTMLButtonElement>('[data-testid="project-card"]');
      },
    };
  }

  it("renders the slug uppercase in the title row", () => {
    const api = render({
      slug: "NEXUS-DESIGN-MIGRATION",
      status: "working",
      progress: 47,
      phase: { current: 3, total: 7 },
      nextGateName: "Phase 4 audit",
      costBurnedCents: 1420,
      lastActivity: "8m ago",
    });
    expect(api.title?.textContent).toBe("NEXUS-DESIGN-MIGRATION");
  });

  it("renders the hero stat as <N>% with Inter Black 72px class", () => {
    const api = render({
      slug: "DEMO",
      status: "idle",
      progress: 47,
      phase: { current: 3, total: 7 },
    });
    expect(api.hero?.textContent).toBe("47%");
    expect(api.hero?.className).toContain("font-black");
    expect(api.hero?.className).toContain("text-[72px]");
    expect(api.hero?.className).toContain("text-primary");
    expect(api.hero?.className).toContain("leading-none");
  });

  it("renders an em-dash when progress is null (no milestone data)", () => {
    const api = render({
      slug: "DEMO",
      status: "idle",
      progress: null,
    });
    expect(api.hero?.textContent).toBe("—%");
  });

  it("renders sub-line in the 'Phase N of M · Next gate: {name}' format", () => {
    const api = render({
      slug: "DEMO",
      status: "idle",
      progress: 50,
      phase: { current: 3, total: 7 },
      nextGateName: "Phase 4 audit",
    });
    expect(api.subline?.textContent).toBe("Phase 3 of 7 · Next gate: Phase 4 audit");
  });

  it("uses '—' placeholders in sub-line when data is missing", () => {
    const api = render({
      slug: "DEMO",
      status: "idle",
      progress: null,
    });
    expect(api.subline?.textContent).toBe("Phase — of — · Next gate: —");
  });

  it("renders footer with USD cost and last activity", () => {
    const api = render({
      slug: "DEMO",
      status: "idle",
      progress: 25,
      costBurnedCents: 1420,
      lastActivity: "8m ago",
    });
    expect(api.footer?.textContent).toBe("$14.20 burned · last activity 8m ago");
  });

  it("uses '—' cost placeholder when cost data is missing", () => {
    const api = render({
      slug: "DEMO",
      status: "idle",
      progress: 25,
    });
    expect(api.footer?.textContent).toBe("— burned · last activity —");
  });

  it("renders a forest-green dot for idle status", () => {
    const api = render({ slug: "A", status: "idle", progress: 0 });
    expect(api.dot?.dataset.status).toBe("idle");
    expect(api.dot?.className).toContain("bg-[#166534]");
  });

  it("renders a pulsing volt dot for working status", () => {
    const api = render({ slug: "A", status: "working", progress: 0 });
    expect(api.dot?.dataset.status).toBe("working");
    expect(api.dot?.className).toContain("bg-primary");
    expect(api.dot?.className).toContain("animate-pulse");
  });

  it("renders a pale-yellow dot for waiting status", () => {
    const api = render({ slug: "A", status: "waiting", progress: 0 });
    expect(api.dot?.dataset.status).toBe("waiting");
    expect(api.dot?.className).toContain("bg-[#f4f692]");
  });

  it("sets progress bar fill width from normal progress value", () => {
    const api = render({ slug: "A", status: "idle", progress: 47 });
    expect(api.fill?.style.width).toBe("47%");
  });

  it("clamps progress above 100 to 100", () => {
    const api = render({ slug: "A", status: "idle", progress: 150 });
    expect(api.fill?.style.width).toBe("100%");
  });

  it("clamps negative progress to 0", () => {
    const api = render({ slug: "A", status: "idle", progress: -10 });
    expect(api.fill?.style.width).toBe("0%");
  });

  it("renders 0-width fill when progress is null", () => {
    const api = render({ slug: "A", status: "idle", progress: null });
    expect(api.fill?.style.width).toBe("0%");
  });

  it("fires onClick when the card button is clicked", () => {
    const onClick = vi.fn();
    const api = render({ slug: "A", status: "idle", progress: 0, onClick });
    act(() => {
      api.button?.click();
    });
    expect(onClick).toHaveBeenCalledTimes(1);
  });

  it("uses slug as aria-label when no override provided", () => {
    const api = render({ slug: "DEMO", status: "idle", progress: 0 });
    expect(api.button?.getAttribute("aria-label")).toBe("DEMO");
  });

  it("accepts an aria-label override", () => {
    const api = render({
      slug: "DEMO",
      status: "idle",
      progress: 0,
      ariaLabel: "Open project DEMO",
    });
    expect(api.button?.getAttribute("aria-label")).toBe("Open project DEMO");
  });
});
