import { useEffect, useState, type ReactNode } from "react";
import type { Scenario, SolvencySnapshot } from "../data/simulation";

/* ---------------- theme ---------------- */

export type Theme = "dark" | "light";

/** In-memory theme only — nothing is persisted to browser storage or cookies. */
export function useTheme(): [Theme, () => void] {
  // Dark-first by design. A viewer who wants the warm light mode toggles it;
  // the choice is intentionally not persisted anywhere.
  const [theme, setTheme] = useState<Theme>("dark");

  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
  }, [theme]);

  return [theme, () => setTheme((t) => (t === "dark" ? "light" : "dark"))];
}

export function ThemeToggle({
  theme,
  onToggle,
}: {
  theme: Theme;
  onToggle: () => void;
}) {
  return (
    <button
      type="button"
      className="icon-btn"
      onClick={onToggle}
      data-testid="theme-toggle"
      aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
      title={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
    >
      {theme === "dark" ? (
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" aria-hidden="true">
          <circle cx="12" cy="12" r="4.4" stroke="currentColor" strokeWidth="1.6" />
          <path
            d="M12 2.6v2.2M12 19.2v2.2M2.6 12h2.2M19.2 12h2.2M5.3 5.3l1.6 1.6M17.1 17.1l1.6 1.6M18.7 5.3l-1.6 1.6M6.9 17.1l-1.6 1.6"
            stroke="currentColor"
            strokeWidth="1.6"
            strokeLinecap="round"
          />
        </svg>
      ) : (
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" aria-hidden="true">
          <path
            d="M20 14.4A8.5 8.5 0 0 1 9.6 4a8.5 8.5 0 1 0 10.4 10.4z"
            stroke="currentColor"
            strokeWidth="1.6"
            strokeLinejoin="round"
          />
        </svg>
      )}
    </button>
  );
}

/* ---------------- primitives ---------------- */

export function Badge({
  children,
  tone = "neutral",
  testId,
}: {
  children: ReactNode;
  tone?: "neutral" | "accent" | "warn" | "danger";
  testId?: string;
}) {
  const cls =
    tone === "accent"
      ? "badge badge-accent"
      : tone === "warn"
        ? "badge badge-warn"
        : tone === "danger"
          ? "badge badge-danger"
          : "badge";
  return (
    <span className={cls} data-testid={testId}>
      {children}
    </span>
  );
}

export function Panel({
  title,
  aside,
  children,
  testId,
}: {
  title?: string;
  aside?: ReactNode;
  children: ReactNode;
  testId?: string;
}) {
  return (
    <section className="panel" data-testid={testId}>
      {title && (
        <div className="panel-head">
          <h3>{title}</h3>
          {aside}
        </div>
      )}
      {children}
    </section>
  );
}

export function CodeBlock({
  label,
  code,
  testId,
}: {
  label: string;
  code: string;
  testId?: string;
}) {
  return (
    <div className="codeblock">
      <div className="codeblock-head">
        <span>{label}</span>
      </div>
      <pre data-testid={testId} tabIndex={0} aria-label={label}>
        {code}
      </pre>
    </div>
  );
}

/* ---------------- data-source blocking ---------------- */

/**
 * Rendered whenever `dataSourceErrors` is non-empty. Errors are shown verbatim
 * and every selection/signing control in the app is disabled while it is up.
 */
export function DataSourceBlock({ solvency }: { solvency: SolvencySnapshot }) {
  return (
    <div
      className="notice notice-danger"
      role="alert"
      data-testid="data-source-error-block"
    >
      <h3>Migration blocked — solvency data source failed</h3>
      <p>
        <code>/api/v3/protocol/solvency</code> did not return usable data, so this
        console cannot verify that V3 assets cover V3 liabilities. Position
        selection and signing stay disabled until the feed recovers. Your V2
        positions are untouched and V2 remains live.
      </p>
      <ul className="notice-list" data-testid="data-source-error-list">
        {solvency.dataSourceErrors.map((e) => (
          <li key={e}>{e}</li>
        ))}
      </ul>
      <p className="small" style={{ marginTop: 10 }}>
        block: {solvency.blockNumber ?? "unavailable"} · finality:{" "}
        {solvency.finality} · observed {solvency.observedAt} · schema{" "}
        {solvency.schemaVersion}
      </p>
    </div>
  );
}

export function SolvencyBar({ scenario }: { scenario: Scenario }) {
  const ok = scenario.solvency.dataSourceErrors.length === 0;
  return (
    <div
      className={ok ? "notice notice-accent" : "notice notice-danger"}
      data-testid="solvency-bar"
    >
      <div style={{ display: "flex", gap: 10, flexWrap: "wrap", alignItems: "center" }}>
        <Badge tone={ok ? "accent" : "danger"} testId="solvency-state">
          {ok ? "solvency feed ok" : "solvency feed failing"}
        </Badge>
        <span className="mono small">
          chainId {scenario.solvency.chainId} · block{" "}
          {scenario.solvency.blockNumber ?? "—"} · finality{" "}
          {scenario.solvency.finality} · dataSourceErrors{" "}
          {scenario.solvency.dataSourceErrors.length}
        </span>
      </div>
    </div>
  );
}

/** Standing disclosures repeated on every step. */
export function StandingDisclosures() {
  return (
    <ul className="list-clean" data-testid="standing-disclosures">
      <li>
        <strong>V2 remains live and unchanged.</strong> No V2 contract is paused,
        upgraded, or reconfigured by migrating. Declining costs you nothing, now
        or later.
      </li>
      <li>
        <strong>Base pilot only, hard caps.</strong> V3 writes are allowlisted on
        Base (chainId 8453) with per-contract caps from{" "}
        <code>BASE_PILOT_CONFIG.md</code>. Amounts above a cap stay in V2.
      </li>
      <li>
        <strong>Mutual aid is not insurance.</strong>{" "}
        <code>MutualAidPoolV3</code> is discretionary mutual aid: no policy, no
        entitlement, no guaranteed payout.
      </li>
      <li>
        <strong>Simulation only.</strong> No wallet is connected, no signature is
        requested, no transaction is broadcast, and every address shown is fake
        demo data.
      </li>
    </ul>
  );
}
