"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { Zap, Pencil, Check, Plus, X, Search, ChevronUp, ChevronDown } from "lucide-react";
import { sfx } from "@/lib/sfx";
import { STOCKS, type CatalogAsset } from "@/lib/market-catalog";
import clsx from "clsx";

type Asset = CatalogAsset;

/** Everything selectable in the Add search. */
/** Assets the person has added, resolved from search or the stock list. */
const DEFAULTS: Asset[] = [
  { key: "cg:bitcoin", sym: "BTC", name: "Bitcoin", type: "crypto", icon: "₿", color: "#f7931a", base: 97000 },
  { key: "cg:ethereum", sym: "ETH", name: "Ethereum", type: "crypto", icon: "Ξ", color: "#627eea", base: 3400 },
  { key: "cg:dogecoin", sym: "DOGE", name: "Dogecoin", type: "crypto", icon: "Ð", color: "#c2a633", base: 0.38 },
  ...STOCKS.filter((s) => ["gold", "aapl"].includes(s.key)),
];

const DEFAULT_KEYS = DEFAULTS.map((a) => a.key);
const STORAGE_KEY = "xrcoin-markets";

function fmt(p: number) {
  if (!p) return "$0.00";
  // Sub-cent assets (memecoins) need far more places than 2dp allows.
  let dp = 2;
  if (p < 0.00001) dp = 10;
  else if (p < 0.001) dp = 8;
  else if (p < 0.1) dp = 6;
  else if (p < 10) dp = 4;
  return `$${p.toLocaleString("en-US", {
    minimumFractionDigits: Math.min(dp, 2),
    maximumFractionDigits: dp,
  })}`;
}

/** Seeds a plausible price history so sparklines aren't flat on load. */
function seed(base: number) {
  let v = base;
  const out: number[] = [];
  for (let i = 0; i < 30; i++) {
    v *= 1 + (Math.random() - 0.5) * 0.012;
    out.push(v);
  }
  return out;
}

function Spark({ hist }: { hist: number[] }) {
  const ref = useRef<HTMLCanvasElement>(null);
  useEffect(() => {
    const c = ref.current;
    if (!c || hist.length < 2) return;
    const x = c.getContext("2d");
    if (!x) return;
    const w = c.width;
    const h = c.height;
    x.clearRect(0, 0, w, h);
    const mn = Math.min(...hist);
    const mx = Math.max(...hist);
    const rg = mx - mn || 1;
    const up = hist[hist.length - 1] >= hist[0];
    const col = up ? "#2fd573" : "#ff4d5a";
    x.beginPath();
    hist.forEach((p, i) => {
      const px = (i / (hist.length - 1)) * w;
      const py = h - 4 - ((p - mn) / rg) * (h - 8);
      if (i) x.lineTo(px, py);
      else x.moveTo(px, py);
    });
    x.strokeStyle = col;
    x.lineWidth = 1.5;
    x.stroke();
    const ly = h - 4 - ((hist[hist.length - 1] - mn) / rg) * (h - 8);
    x.beginPath();
    x.arc(w - 2, ly, 2, 0, 7);
    x.fillStyle = col;
    x.shadowColor = col;
    x.shadowBlur = 6;
    x.fill();
  }, [hist]);
  return <canvas ref={ref} className="mk-spark" width={58} height={26} />;
}

type Live = { price: number; anchor: number; change: number; hist: number[]; dir: 0 | 1 | -1 };

export function MarketsWidget() {
  const [assets, setAssets] = useState<Asset[]>(DEFAULTS);
  const [live, setLive] = useState<Record<string, Live>>({});
  const [editing, setEditing] = useState(false);
  const [adding, setAdding] = useState(false);
  const [found, setFound] = useState<Asset[]>([]);
  const [query, setQuery] = useState("");

  // Restore the person's own watchlist.
  useEffect(() => {
    try {
      const saved = localStorage.getItem(STORAGE_KEY);
      if (saved) {
        const parsed = JSON.parse(saved);
        if (Array.isArray(parsed) && parsed.length && typeof parsed[0] === "object") {
          setAssets(parsed);
        }
      }
    } catch {
      /* keep defaults */
    }
  }, []);

  const persist = useCallback((next: Asset[]) => {
    setAssets(next);
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
    } catch {
      /* non-fatal */
    }
  }, []);

  // Real quotes where we can get them; simulated drift in between so the
  // widget still feels live without hammering the upstream rate limit.
  const fetchPrices = useCallback(async () => {
    if (assets.length === 0) return;
    const res = await fetch(
      `/api/markets/prices?keys=${assets.map((a) => a.key).join(",")}`
    ).catch(() => null);
    if (!res || !res.ok) return;
    const d = await res.json();
    setLive((prev) => {
      const next = { ...prev };
      for (const a of assets) {
        const q = d.prices?.[a.key];
        const price = q?.price || prev[a.key]?.price || a.base;
        const hist = prev[a.key]?.hist ?? seed(price);
        next[a.key] = {
          price,
          anchor: price,
          change: q?.change ?? prev[a.key]?.change ?? 0,
          hist: [...hist, price].slice(-36),
          dir: prev[a.key] ? (price >= prev[a.key].price ? 1 : -1) : 0,
        };
      }
      return next;
    });
  }, [assets]);

  useEffect(() => {
    fetchPrices();
    const id = setInterval(fetchPrices, 60000);
    return () => clearInterval(id);
  }, [fetchPrices]);

  // Between fetches, nudge prices so the sparkline keeps moving.
  useEffect(() => {
    const id = setInterval(() => {
      setLive((prev) => {
        const next: Record<string, Live> = {};
        for (const [k, v] of Object.entries(prev)) {
          // Jitter around the anchor, so the displayed value never strays
          // more than ~0.1% from the real quote.
          const price = v.anchor * (1 + (Math.random() - 0.5) * 0.002);
          next[k] = {
            price,
            anchor: v.anchor,
            change: v.change,
            hist: [...v.hist, price].slice(-36),
            dir: price >= v.price ? 1 : -1,
          };
        }
        return next;
      });
    }, 4000);
    return () => clearInterval(id);
  }, []);

  // Search hits the API so every coin is reachable, not just a fixed list.
  useEffect(() => {
    if (!adding) return;
    const t = setTimeout(() => {
      fetch(`/api/markets/search?q=${encodeURIComponent(query)}`)
        .then((r) => (r.ok ? r.json() : { results: [] }))
        .then((d) => setFound(d.results ?? []))
        .catch(() => setFound([]));
    }, 250);
    return () => clearTimeout(t);
  }, [query, adding]);

  /** Swap an asset with its neighbour, so the list can be reordered. */
  const move = useCallback(
    (index: number, dir: -1 | 1) => {
      const next = [...assets];
      const target = index + dir;
      if (target < 0 || target >= next.length) return;
      [next[index], next[target]] = [next[target], next[index]];
      persist(next);
      sfx.click();
    },
    [assets, persist]
  );

  const rows = assets;
  const available = found.filter((a) => !assets.some((x) => x.key === a.key));

  return (
    <div className="mkw">
      <div className="mk-head">
        <span className="mk-title flex items-center gap-1.5">
          <Zap size={14} className="text-amber-500 shrink-0" />
          Live Prices
        </span>
        <span className="mk-live">
          <i /> LIVE
        </span>
        <button
          className="mk-btn"
          onClick={() => {
            setEditing((v) => !v);
            setAdding(false);
            sfx.click();
          }}
          title={editing ? "Done" : "Edit list"}
        >
          {editing ? <Check size={12} /> : <Pencil size={11} />}
        </button>
      </div>

      {rows.map((a, i) => {
        const l = live[a.key];
        const chg = l?.change ?? 0;
        return (
          <div key={a.key} className={clsx("mk-row", l?.dir === 1 && "up", l?.dir === -1 && "down")}>
            <span className="mk-coin" style={{ ["--c" as string]: a.color }}>
              {a.image ? (
                // eslint-disable-next-line @next/next/no-img-element
                <img src={a.image} alt="" className="w-full h-full rounded-full object-cover" />
              ) : (
                a.icon
              )}
            </span>
            <span className="mk-info">
              <b>{a.name}</b>
              <span>{a.sym}</span>
            </span>
            {!editing && <Spark hist={l?.hist ?? []} />}
            {editing ? (
              <span className="flex items-center gap-1 shrink-0">
                <button
                  className="mk-move"
                  disabled={i === 0}
                  onClick={() => move(i, -1)}
                  title="Move up"
                >
                  <ChevronUp size={12} />
                </button>
                <button
                  className="mk-move"
                  disabled={i === rows.length - 1}
                  onClick={() => move(i, 1)}
                  title="Move down"
                >
                  <ChevronDown size={12} />
                </button>
                <button
                  className="mk-del"
                  onClick={() => {
                    persist(assets.filter((x) => x.key !== a.key));
                    sfx.unsave();
                  }}
                  title={`Remove ${a.name}`}
                >
                  <X size={12} />
                </button>
              </span>
            ) : (
              <span className="mk-price">
                <span className={clsx("mk-p", l?.dir === 1 && "tick-up", l?.dir === -1 && "tick-down")}>
                  {l ? fmt(l.price) : "—"}
                </span>
                <span className={clsx("mk-cn", chg >= 0 ? "up" : "down")}>
                  {chg >= 0 ? "▲" : "▼"}
                  {Math.abs(chg).toFixed(2)}%
                </span>
              </span>
            )}
          </div>
        );
      })}

      {adding ? (
        <div>
          <div className="relative">
            <input
              autoFocus
              className="mk-search"
              placeholder="Search stocks, crypto, metals..."
              value={query}
              onChange={(e) => setQuery(e.target.value)}
            />
            <Search
              size={12}
              className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-400 pointer-events-none"
            />
          </div>
          <div className="max-h-44 overflow-y-auto">
            {available.slice(0, 30).map((a) => (
              <button
                key={a.key}
                className="mk-opt"
                onClick={() => {
                  persist([...assets, a]);
                  setQuery("");
                  setAdding(false);
                  sfx.friend();
                }}
              >
                <span className="mk-coin" style={{ ["--c" as string]: a.color, width: 24, height: 24, fontSize: 11 }}>
                  {a.icon}
                </span>
                <span className="flex-1 min-w-0">
                  <span className="block text-xs font-semibold truncate">{a.name}</span>
                  <span className="block text-[10px] text-neutral-400">{a.sym}</span>
                </span>
                <Plus size={13} className="text-emerald-500" />
              </button>
            ))}
            {available.length === 0 && (
              <p className="text-[11px] text-neutral-400 py-2 px-1">Nothing else to add.</p>
            )}
          </div>
          <button className="mk-add mt-1" onClick={() => setAdding(false)}>
            Cancel
          </button>
        </div>
      ) : (
        <button
          className="mk-add"
          onClick={() => {
            setAdding(true);
            sfx.click();
          }}
        >
          + Add asset
        </button>
      )}
    </div>
  );
}
