import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
  AppHeader,
  BarChart,
  DataTable,
  EvidenceList,
  KpiStrip,
  RefreshBar,
  SourceStatusList,
  StackedBarChart,
  formatCurrency,
} from "../index.js";

afterEach(cleanup);

describe("@thinkwork/computer-stdlib primitives", () => {
  it("renders KPI cards and shared formatters", () => {
    render(
      <KpiStrip
        cards={[
          {
            label: "Open pipeline",
            value: formatCurrency(1_250_000),
            detail: "12 opportunities",
            tone: "success",
          },
          {
            label: "High-risk exposure",
            value: formatCurrency(310_000),
            detail: "3 high-risk deals",
            tone: "risk",
          },
        ]}
      />,
    );

    expect(screen.getByText("Open pipeline")).toBeTruthy();
    expect(screen.getByText("$1.3M")).toBeTruthy();
    expect(screen.getByText("High-risk exposure")).toBeTruthy();
  });

  it("renders no empty KPI grid when there are no cards", () => {
    const { container } = render(<KpiStrip cards={[]} />);

    expect(container.firstChild).toBeNull();
  });

  it("accepts generated dashboard KPI aliases", () => {
    render(
      <KpiStrip
        kpis={[
          {
            label: "High-Risk Exposure",
            value: formatCurrency(1_258_000),
            detail: "4 high-risk deals",
            tone: "risk",
          },
        ]}
      />,
    );

    expect(screen.getByText("High-Risk Exposure")).toBeTruthy();
    expect(screen.getByText("$1.3M")).toBeTruthy();
  });

  it("renders header provenance and badges", () => {
    render(
      <AppHeader
        title="Meeting prep"
        summary="Agenda and account notes"
        badges={[{ label: "Read-only", variant: "outline" }]}
        generatedAt="2026-05-09T12:00:00.000Z"
      />,
    );

    expect(screen.getByText("Meeting prep")).toBeTruthy();
    expect(screen.getByText("Agenda and account notes")).toBeTruthy();
    expect(screen.getByText("Read-only")).toBeTruthy();
    expect(screen.getByText("Private artifact")).toBeTruthy();
  });

  it("renders table rows and an empty-state slot", () => {
    const { container } = render(
      <DataTable
        title="Opportunity risk"
        columns={[
          { key: "name", header: "Name" },
          {
            key: "amount",
            header: "Amount",
            align: "right",
            render: (value) => formatCurrency(Number(value)),
          },
        ]}
        rows={[{ name: "Renewal", amount: 500_000 }]}
      />,
    );

    expect(screen.getByText("Opportunity risk")).toBeTruthy();
    expect(screen.getByText("Renewal")).toBeTruthy();
    expect(screen.getByText("$500,000")).toBeTruthy();
    expect(
      container.querySelector(".overflow-hidden.rounded-md.border"),
    ).toBeTruthy();
    expect(screen.getByRole("button", { name: "Amount" })).toBeTruthy();

    cleanup();

    render(
      <DataTable
        columns={[{ key: "name", header: "Name" }]}
        rows={[]}
        emptyState="No opportunities"
      />,
    );

    expect(screen.getByText("No opportunities")).toBeTruthy();
  });

  it("sorts table rows from applet column headers", () => {
    render(
      <DataTable
        columns={[
          { key: "name", header: "Name" },
          {
            key: "amount",
            header: "Amount",
            align: "right",
            render: (value) => formatCurrency(Number(value)),
          },
        ]}
        rows={[
          { name: "Small", amount: 100 },
          { name: "Large", amount: 900 },
        ]}
      />,
    );

    fireEvent.click(screen.getByRole("button", { name: "Amount" }));
    fireEvent.click(screen.getByRole("button", { name: /Amount/ }));

    const rows = screen.getAllByRole("row");
    expect(rows[1].textContent).toContain("Small");
    expect(rows[2].textContent).toContain("Large");
  });

  it("renders evidence links and text-only evidence", () => {
    render(
      <EvidenceList
        items={[
          {
            id: "email",
            title: "Email signal",
            snippet: "Prospect asked for legal review",
            sourceId: "gmail",
            fetchedAt: "2026-05-09T12:00:00.000Z",
            url: "https://example.com",
          },
          {
            id: "note",
            title: "Internal note",
            snippet: "No source URL",
          },
        ]}
      />,
    );

    expect(screen.getByText("Email signal")).toBeTruthy();
    expect(screen.getByRole("link", { name: "Source" })).toBeTruthy();
    expect(screen.getByText("Internal note")).toBeTruthy();
  });

  it("accepts generated dashboard evidence aliases", () => {
    render(
      <EvidenceList
        evidence={[
          {
            id: "crm-stale",
            title: "CRM stale signal",
            snippet: "No contact in 65 days",
            sourceId: "crm",
            observedAt: "2026-05-09T12:00:00.000Z",
          },
        ]}
      />,
    );

    expect(screen.getByText("CRM stale signal")).toBeTruthy();
    expect(screen.getByText("No contact in 65 days")).toBeTruthy();
    expect(screen.getByText("crm")).toBeTruthy();
  });

  it("renders source status and chart empty states", () => {
    render(
      <>
        <SourceStatusList
          sources={[
            { id: "crm", label: "CRM", status: "success", recordCount: 5 },
            {
              id: "email",
              label: "Email",
              status: "partial",
              error: "Some messages were skipped",
            },
          ]}
        />
        <BarChart title="Stage exposure" data={[]} />
        <StackedBarChart
          title="Product exposure"
          data={[]}
          segments={[{ key: "risk", label: "Risk", color: "red" }]}
        />
      </>,
    );

    expect(screen.getByText("CRM")).toBeTruthy();
    expect(screen.getByText("partial")).toBeTruthy();
    expect(screen.getAllByText("No chart data yet.")).toHaveLength(2);
  });

  it("renders failed refresh state and hides Ask Computer unless provided", () => {
    const onRefresh = vi.fn();
    render(
      <RefreshBar
        title="Refresh app"
        refreshState="failed"
        sourceStatuses={{ crm: "success", email: "partial" }}
        error="Refresh failed"
        onRefresh={onRefresh}
      />,
    );

    expect(screen.getByText("Refresh app")).toBeTruthy();
    expect(screen.getByText("Failed")).toBeTruthy();
    expect(screen.getByText("Refresh failed")).toBeTruthy();
    expect(screen.getByText("Refresh source status")).toBeTruthy();
    expect(screen.getByText("crm")).toBeTruthy();
    expect(screen.getByText("email")).toBeTruthy();
    expect(screen.queryByRole("button", { name: "Ask Computer" })).toBeNull();
    fireEvent.click(screen.getByRole("button", { name: "Refresh" }));
    expect(onRefresh).toHaveBeenCalledTimes(1);
  });

  it("hides the refresh button when refresh is disabled", () => {
    render(<RefreshBar disabled onRefresh={() => undefined} />);

    expect(screen.queryByRole("button", { name: "Refresh" })).toBeNull();
  });
});
