import { Activity, ArrowDown, ArrowUp, Minus, RefreshCw } from "lucide-react";
import { useEffect, useState, type ReactNode } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
    Sheet,
    SheetContent,
    SheetDescription,
    SheetFooter,
    SheetHeader,
    SheetTitle,
    SheetTrigger,
} from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import {
    Tooltip,
    TooltipContent,
    TooltipTrigger,
} from "@/components/ui/tooltip";
import { usePolledJson } from "@/hooks/use-polled-json";
import {
    formatCompact,
    formatPct,
    formatPrice,
    formatUsd,
    trendColor,
    type MarketType,
} from "@/lib/market";
import { cn } from "@/lib/utils";
import { useI18n } from "@/lib/i18n";
import {
    getSignalDisplayLabel,
    getSignalDisplayState,
} from "@/lib/SignalSnapshot";
import type {
    CommodityQuote,
    GlobalData,
    MacroData,
    MicroData,
    Rating,
    SignalData,
    SignalVote,
    Stablecoin,
    SignalMode,
    SignalSetup,
} from "@/types/insight";

type Tab = "micro" | "macro" | "signal" | "global";

interface CoinInsightSheetProps {
    symbol: string;
    market: MarketType;
    name: string;
    interval: string;
    provider: string;
    signalMode: SignalMode;
    workspaceSignal: SignalSetup | null;
    workspaceSignalStatus: "idle" | "loading" | "success" | "empty" | "error";
    workspaceSignalDataIntegrity: boolean;
    workspaceSignalEnabled: boolean;
    workspaceSignalSummary: { label: string; detail: string };
}

const MICRO_REFRESH_MS = 10_000;
const MACRO_REFRESH_MS = 60_000;
const SIGNAL_REFRESH_MS = 30_000;
const GLOBAL_REFRESH_MS = 60_000;
const DASH = "–";

const usd = (v: number) => "$" + formatPrice(v);
const clamp = (v: number, min = 0, max = 100) =>
    Math.min(max, Math.max(min, v));

function orDash<T>(value: T | null | undefined, format: (v: T) => string) {
    return value === null || value === undefined ? DASH : format(value);
}

function finiteMetric(value: unknown): number | null {
    if (typeof value === "number") {
        return Number.isFinite(value) ? value : null;
    }
    if (typeof value === "string" && value.trim() !== "") {
        const parsed = Number(value);
        return Number.isFinite(parsed) ? parsed : null;
    }
    return null;
}

function formatDate(iso: string | null, locale = "en"): string {
    if (!iso) return DASH;
    const d = new Date(iso);
    return Number.isNaN(d.getTime())
        ? DASH
        : d.toLocaleDateString(locale === "id" ? "id-ID" : "en-US", {
              day: "numeric",
              month: "short",
              year: "numeric",
          });
}

/* ---------------------------- Label sinyal ---------------------------- */

type Signal = { text: string; className: string };

const GOOD =
    "border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400";
const BAD = "border-red-500/30 bg-red-500/10 text-red-600 dark:text-red-400";
const WARN =
    "border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-400";
const NEUTRAL = "border-border bg-muted/60 text-muted-foreground";

function rsiSignal(
    v: number | null,
    t: (key: string, fallback?: string) => string,
): Signal | null {
    if (v === null) return null;
    if (v >= 70)
        return { text: t("insight.overbought", "Overbought"), className: BAD };
    if (v <= 30)
        return { text: t("insight.oversold", "Oversold"), className: GOOD };
    return { text: t("insight.neutral", "Neutral"), className: NEUTRAL };
}

function trendSignal(
    t: MicroData["technical"],
    tr: (key: string, fallback?: string) => string,
): Signal | null {
    const { sma20, sma50, price_vs_sma20_pct: a, price_vs_sma50_pct: b } = t;
    if (sma20 === null || sma50 === null || a === null || b === null)
        return null;
    if (a > 0 && b > 0 && sma20 > sma50)
        return { text: tr("insight.trend_up", "Uptrend"), className: GOOD };
    if (a < 0 && b < 0 && sma20 < sma50)
        return { text: tr("insight.trend_down", "Downtrend"), className: BAD };
    return { text: tr("insight.trend_mixed", "Mixed trend"), className: WARN };
}

function shareSignal(
    pct: number | null,
    buy: string,
    sell: string,
    tr: (key: string, fallback?: string) => string,
): Signal | null {
    if (pct === null) return null;
    if (pct >= 55) return { text: buy, className: GOOD };
    if (pct <= 45) return { text: sell, className: BAD };
    return { text: tr("insight.balanced", "Balanced"), className: NEUTRAL };
}

const FNG_ID: Record<string, string> = {
    "Extreme Fear": "Ketakutan ekstrem",
    Fear: "Takut",
    Neutral: "Netral",
    Greed: "Serakah",
    "Extreme Greed": "Keserakahan ekstrem",
};

function fngColor(value: number): string {
    if (value < 25) return "text-red-500 dark:text-red-400";
    if (value < 45) return "text-orange-500 dark:text-orange-400";
    if (value < 55) return "text-amber-500 dark:text-yellow-300";
    if (value < 75) return "text-lime-600 dark:text-lime-400";
    return "text-emerald-500 dark:text-emerald-400";
}

/* ---------------------------- Blok dasar UI ---------------------------- */

function Hint({ text, children }: { text?: string; children: ReactNode }) {
    if (!text) return <>{children}</>;

    return (
        <Tooltip>
            <TooltipTrigger asChild>
                <button
                    type="button"
                    className="cursor-help border-b border-dotted border-muted-foreground/50 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                >
                    {children}
                </button>
            </TooltipTrigger>
            <TooltipContent
                side="top"
                className="max-w-[16rem] leading-relaxed"
            >
                {text}
            </TooltipContent>
        </Tooltip>
    );
}

function Section({ title, children }: { title: string; children: ReactNode }) {
    return (
        <section className="space-y-2">
            <h3 className="text-xs font-semibold tracking-wider text-muted-foreground uppercase">
                {title}
            </h3>
            {children}
        </section>
    );
}

function Metric({
    label,
    hint,
    className,
    children,
}: {
    label: string;
    hint?: string;
    className?: string;
    children: ReactNode;
}) {
    return (
        <div className="min-w-0 rounded-lg border border-border/60 bg-muted/30 p-3 transition-colors">
            <div className="mb-1 text-xs text-muted-foreground">
                <Hint text={hint}>{label}</Hint>
            </div>
            <div
                className={cn(
                    "truncate text-sm font-semibold text-foreground tabular-nums",
                    className,
                )}
            >
                {children}
            </div>
        </div>
    );
}

function SignalBadge({
    label,
    signal,
}: {
    label: string;
    signal: Signal | null;
}) {
    if (!signal) return null;

    return (
        <Badge
            variant="outline"
            className={cn("px-2 py-0.5 text-xs font-medium", signal.className)}
        >
            <span className="opacity-70 mr-1">{label}</span> {signal.text}
        </Badge>
    );
}

function SplitBar({ leftPct }: { leftPct: number }) {
    const left = clamp(leftPct);
    const { t } = useI18n();

    return (
        <div
            className="flex h-2 overflow-hidden rounded-full bg-muted"
            role="img"
            aria-label={`${left.toFixed(1)}% ${t("insight.buy", "Buy")}, ${(100 - left).toFixed(1)}% ${t("insight.sell", "Sell")}`}
        >
            <div className="bg-emerald-500" style={{ width: `${left}%` }} />
            <div className="bg-red-500" style={{ width: `${100 - left}%` }} />
        </div>
    );
}

function PanelSkeleton() {
    const { t } = useI18n();
    return (
        <div
            className="space-y-5"
            aria-busy="true"
            aria-label={t("common.loading", "Loading data")}
        >
            <Skeleton className="h-24 w-full" />
            <div className="grid grid-cols-2 gap-2">
                {Array.from({ length: 6 }).map((_, i) => (
                    <Skeleton key={i} className="h-16" />
                ))}
            </div>
            <Skeleton className="h-28 w-full" />
        </div>
    );
}

function ErrorNote({
    message,
    hasData,
    onRetry,
    retrying,
}: {
    message: string;
    hasData: boolean;
    onRetry?: () => void;
    retrying?: boolean;
}) {
    const { t } = useI18n();
    return (
        <div
            role="alert"
            className="flex items-center justify-between gap-3 rounded-lg border border-destructive/50 bg-destructive/10 px-3 py-2 text-xs text-destructive"
        >
            <span>
                {message}
                {hasData
                    ? ` ${t("common.showing_last_data", "Showing the last successfully loaded data.")}`
                    : ` ${t("common.retry_automatically", "Will retry automatically.")}`}
            </span>
            {onRetry && (
                <button
                    type="button"
                    onClick={onRetry}
                    disabled={retrying}
                    className="inline-flex shrink-0 items-center gap-1 rounded-md border border-destructive/30 px-2 py-1 font-medium hover:bg-destructive/10 disabled:opacity-50"
                >
                    <RefreshCw
                        className={cn("size-3", retrying && "animate-spin")}
                    />
                    {t("common.retry", "Retry")}
                </button>
            )}
        </div>
    );
}

function Unavailable({ children }: { children: ReactNode }) {
    return (
        <p className="rounded-lg border border-dashed border-border px-3 py-4 text-center text-xs text-muted-foreground">
            {children}
        </p>
    );
}

/* ------------------------------- Data mikro ------------------------------ */

function MicroPanel({ data, base }: { data: MicroData; base: string }) {
    const { t } = useI18n();
    const { price, change, volume, book, technical: tech } = data;
    const vsVwap = price.vwap ? (price.last / price.vwap - 1) * 100 : null;
    const rsi = rsiSignal(tech.rsi14, t);

    return (
        <div className="space-y-5">
            <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                <div className="mb-3 flex items-baseline justify-between gap-2">
                    <span className="text-2xl font-semibold text-foreground tabular-nums">
                        {usd(price.last)}
                    </span>
                    <span
                        className={cn(
                            "text-sm font-medium tabular-nums",
                            trendColor(change["24h"]),
                        )}
                    >
                        {formatPct(change["24h"])}
                        <span className="ml-1 text-xs text-muted-foreground">
                            {t("insight.hours_24", "24 hours")}
                        </span>
                    </span>
                </div>

                <div className="relative h-1.5 rounded-full bg-muted">
                    <div
                        className="absolute top-1/2 size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background bg-primary"
                        style={{ left: `${clamp(price.range_pos_pct)}%` }}
                    />
                </div>
                <div className="mt-2 flex justify-between text-xs text-muted-foreground tabular-nums">
                    <span>
                        {t("insight.low", "Low")} {usd(price.low)}
                    </span>
                    <span>
                        {t("insight.high", "High")} {usd(price.high)}
                    </span>
                </div>
            </div>

            <div className="flex flex-wrap gap-1.5">
                <SignalBadge
                    label={t("insight.trend_1h", "1h Trend")}
                    signal={trendSignal(tech, t)}
                />
                <SignalBadge label={t("insight.rsi", "RSI")} signal={rsi} />
                <SignalBadge
                    label={t("insight.taker", "Taker")}
                    signal={shareSignal(
                        volume.taker_buy_ratio,
                        t("insight.buy_dominant", "Buy dominant"),
                        t("insight.sell_dominant", "Sell dominant"),
                        t,
                    )}
                />
                <SignalBadge
                    label={t("insight.order_book", "Order book")}
                    signal={shareSignal(
                        book?.bid_share_pct ?? null,
                        t("insight.bid_dominant", "Bid dominant"),
                        t("insight.ask_dominant", "Ask dominant"),
                        t,
                    )}
                />
            </div>

            <Section title={t("insight.price_momentum", "Momentum harga")}>
                <div className="grid grid-cols-4 gap-2">
                    {(
                        [
                            [t("insight.time_1h", "1 hour"), change["1h"]],
                            [t("insight.time_4h", "4 hours"), change["4h"]],
                            [t("insight.time_24h", "24 hours"), change["24h"]],
                            [t("insight.time_7d", "7 days"), change["7d"]],
                        ] as const
                    ).map(([label, value]) => (
                        <div
                            key={label}
                            className="rounded-lg border border-border/60 bg-muted/30 p-2 text-center"
                        >
                            <div className="text-[11px] text-muted-foreground">
                                {label}
                            </div>
                            <div
                                className={cn(
                                    "text-sm font-semibold tabular-nums",
                                    trendColor(value),
                                )}
                            >
                                {orDash(value, (v) => formatPct(v))}
                            </div>
                        </div>
                    ))}
                </div>
            </Section>

            <Section
                title={t("insight.volume_activity", "Volume & activity (24h)")}
            >
                <div className="grid grid-cols-2 gap-2">
                    <Metric
                        label={t("insight.volume_base", "Volume :base", {
                            base,
                        })}
                        hint={t(
                            "insight.volume_base_hint",
                            "Amount of the asset traded over the last 24 hours on Binance.",
                        )}
                    >
                        {formatCompact(volume.base)}
                    </Metric>
                    <Metric
                        label={t("insight.volume_usdt", "Volume USDT")}
                        hint={t(
                            "insight.volume_usdt_hint",
                            "Trading value over the last 24 hours in USDT.",
                        )}
                    >
                        {formatUsd(volume.quote)}
                    </Metric>
                    <Metric
                        label={t("insight.trade_count", "Trade count")}
                        hint={t(
                            "insight.trade_count_hint",
                            "Number of trades executed over the last 24 hours.",
                        )}
                    >
                        {orDash(volume.trades, (v) => v.toLocaleString("en-US"))}
                    </Metric>
                    <Metric
                        label={t("insight.price_vs_vwap", "Price vs VWAP")}
                        hint={t(
                            "insight.price_vs_vwap_hint",
                            "VWAP is the 24-hour volume-weighted average price.",
                        )}
                        className={trendColor(vsVwap)}
                    >
                        {price.vwap
                            ? `${usd(price.vwap)} (${formatPct(vsVwap ?? 0)})`
                            : DASH}
                    </Metric>
                </div>

                <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                    <div className="mb-2 flex items-center justify-between text-xs">
                        <Hint
                            text={t(
                                "insight.buy_pressure_hint",
                                "Share of volume from buyer takers. Above 50% indicates more aggressive buying.",
                            )}
                        >
                            <span className="text-muted-foreground">
                                {t(
                                    "insight.buy_pressure",
                                    "Buy pressure (taker)",
                                )}
                            </span>
                        </Hint>
                        <span className="font-semibold text-foreground tabular-nums">
                            {orDash(
                                volume.taker_buy_ratio,
                                (v) => `${v.toFixed(1)}%`,
                            )}
                        </span>
                    </div>
                    {volume.taker_buy_ratio !== null && (
                        <SplitBar leftPct={volume.taker_buy_ratio} />
                    )}
                </div>
            </Section>

            <Section
                title={t(
                    "insight.order_book_100",
                    "Order book (100 level teratas)",
                )}
            >
                {book ? (
                    <>
                        <div className="grid grid-cols-2 gap-2">
                            <Metric
                                label={t("insight.best_bid", "Best bid")}
                                hint={t(
                                    "insight.best_bid_hint",
                                    "Highest price posted by buyers.",
                                )}
                                className="text-emerald-500 dark:text-emerald-400"
                            >
                                {usd(book.bid)}
                            </Metric>
                            <Metric
                                label={t("insight.best_ask", "Best ask")}
                                hint={t(
                                    "insight.best_ask_hint",
                                    "Lowest price posted by sellers.",
                                )}
                                className="text-red-500 dark:text-red-400"
                            >
                                {usd(book.ask)}
                            </Metric>
                            <Metric
                                label={t("insight.spread", "Spread")}
                                hint={t(
                                    "insight.spread_hint",
                                    "Difference between ask and bid.",
                                )}
                            >
                                {orDash(
                                    book.spread_bps,
                                    (v) => `${v.toFixed(2)} bps`,
                                )}
                                <span className="ml-1 text-xs font-normal text-muted-foreground">
                                    ({usd(book.spread)})
                                </span>
                            </Metric>
                            <Metric
                                label={t("insight.bid_ask", "Bid : Ask")}
                                hint={t(
                                    "insight.bid_ask_hint",
                                    "Comparison of buy and sell order value.",
                                )}
                            >
                                {book.bid_share_pct.toFixed(1)} :{" "}
                                {(100 - book.bid_share_pct).toFixed(1)}
                            </Metric>
                        </div>

                        <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                            <div className="mb-2 flex justify-between text-xs tabular-nums">
                                <span className="text-emerald-500 dark:text-emerald-400 font-medium">
                                    Bid {formatUsd(book.bid_value)}
                                </span>
                                <span className="text-red-500 dark:text-red-400 font-medium">
                                    Ask {formatUsd(book.ask_value)}
                                </span>
                            </div>
                            <SplitBar leftPct={book.bid_share_pct} />
                        </div>

                        <div className="grid grid-cols-2 gap-2">
                            <Metric
                                label={t("insight.buy_wall", "Buy wall")}
                                hint={t(
                                    "insight.buy_wall_hint",
                                    "Bid level with the largest value.",
                                )}
                                className="text-emerald-500 dark:text-emerald-400"
                            >
                                {orDash(book.bid_wall.price, usd)}
                                <span className="ml-1 text-xs font-normal text-muted-foreground">
                                    {formatUsd(book.bid_wall.value)}
                                </span>
                            </Metric>
                            <Metric
                                label={t("insight.sell_wall", "Sell wall")}
                                hint={t(
                                    "insight.sell_wall_hint",
                                    "Ask level with the largest value.",
                                )}
                                className="text-red-500 dark:text-red-400"
                            >
                                {orDash(book.ask_wall.price, usd)}
                                <span className="ml-1 text-xs font-normal text-muted-foreground">
                                    {formatUsd(book.ask_wall.value)}
                                </span>
                            </Metric>
                        </div>
                    </>
                ) : (
                    <Unavailable>
                        {t(
                            "insight.order_book_unavailable",
                            "Order book is not available yet.",
                        )}
                    </Unavailable>
                )}
            </Section>

            <Section
                title={t("insight.technical_1h", "Teknikal (candle 1 jam)")}
            >
                <div className="grid grid-cols-2 gap-2">
                    <Metric
                        label="RSI 14"
                        hint={t(
                            "insight.rsi_hint",
                            "Momentum indicator from 0–100.",
                        )}
                    >
                        {orDash(tech.rsi14, (v) => v.toFixed(1))}
                        {rsi && (
                            <Badge
                                variant="outline"
                                className={cn(
                                    "ml-2 align-middle px-1.5 py-0 text-[10px]",
                                    rsi.className,
                                )}
                            >
                                {rsi.text}
                            </Badge>
                        )}
                    </Metric>
                    <Metric
                        label={t("insight.atr14", "ATR 14")}
                        hint={t(
                            "insight.atr14_hint",
                            "Average movement range per candle.",
                        )}
                    >
                        {orDash(tech.atr14_pct, (v) => `${v.toFixed(2)}%`)}
                    </Metric>
                    <Metric
                        label={t("insight.price_vs_sma20", "Price vs SMA 20")}
                        hint={t(
                            "insight.price_vs_sma20_hint",
                            "Distance from the 20-candle moving average.",
                        )}
                        className={trendColor(tech.price_vs_sma20_pct)}
                    >
                        {orDash(tech.price_vs_sma20_pct, (v) => formatPct(v))}
                    </Metric>
                    <Metric
                        label={t("insight.price_vs_sma50", "Price vs SMA 50")}
                        hint={t(
                            "insight.price_vs_sma50_hint",
                            "Distance from the 50-candle moving average.",
                        )}
                        className={trendColor(tech.price_vs_sma50_pct)}
                    >
                        {orDash(tech.price_vs_sma50_pct, (v) => formatPct(v))}
                    </Metric>
                    <Metric
                        label={t("insight.volatility_24h", "24h volatility")}
                        hint={t(
                            "insight.volatility_24h_hint",
                            "Standard deviation of hourly returns.",
                        )}
                    >
                        {orDash(
                            tech.volatility_24h_pct,
                            (v) =>
                                `${v.toFixed(2)}% / ${t("insight.hour", "hour")}`,
                        )}
                    </Metric>
                </div>
            </Section>
        </div>
    );
}

/* ------------------------------- Data makro ------------------------------ */

function MacroPanel({ data, name }: { data: MacroData; name: string }) {
    const { t, locale } = useI18n();
    const { coin, global: glob, sentiment } = data;

    return (
        <div className="space-y-5">
            {coin ? (
                <>
                    <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                        <div className="mb-1 flex items-center justify-between gap-2 text-xs text-muted-foreground">
                            <span>Kapitalisasi pasar</span>
                            {coin.rank !== null && (
                                <Badge variant="outline" className={NEUTRAL}>
                                    Peringkat #{coin.rank}
                                </Badge>
                            )}
                        </div>
                        <div className="text-2xl font-semibold text-foreground tabular-nums">
                            {orDash(coin.market_cap, formatUsd)}
                        </div>
                        <div className="mt-1 text-xs text-muted-foreground">
                            {name} ·{" "}
                            <span className={trendColor(coin.change["24h"])}>
                                {orDash(coin.change["24h"], (v) =>
                                    formatPct(v),
                                )}{" "}
                                (24 jam)
                            </span>
                        </div>
                    </div>

                    <Section
                        title={t(
                            "insight.valuation_liquidity",
                            "Valuasi & likuiditas",
                        )}
                    >
                        <div className="grid grid-cols-2 gap-2">
                            <Metric label="FDV" hint="Fully Diluted Valuation.">
                                {orDash(coin.fdv, formatUsd)}
                            </Metric>
                            <Metric
                                label="Volume 24 jam"
                                hint="Total volume lintas bursa (CoinGecko)."
                            >
                                {orDash(coin.volume_24h, formatUsd)}
                            </Metric>
                            <Metric
                                label="Turnover"
                                hint="Volume 24 jam dibagi market cap."
                            >
                                {orDash(
                                    coin.turnover_pct,
                                    (v) => `${v.toFixed(2)}%`,
                                )}
                            </Metric>
                            <Metric
                                label="Dominasi"
                                hint="Porsi market cap dari total kripto."
                            >
                                {orDash(
                                    coin.dominance_pct,
                                    (v) => `${v.toFixed(v < 1 ? 3 : 2)}%`,
                                )}
                            </Metric>
                        </div>
                    </Section>

                    <Section title={t("insight.supply", "Suplai")}>
                        <div className="grid grid-cols-2 gap-2">
                            <Metric
                                label="Beredar"
                                hint="Jumlah koin beredar saat ini."
                            >
                                {orDash(coin.circulating_supply, (v) =>
                                    formatCompact(v),
                                )}
                            </Metric>
                            <Metric label="Maksimum" hint="Batas suplai keras.">
                                {coin.max_supply === null
                                    ? "∞"
                                    : formatCompact(coin.max_supply)}
                            </Metric>
                        </div>
                        {coin.supply_ratio_pct !== null && (
                            <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                                <div className="mb-2 flex justify-between text-xs">
                                    <span className="text-muted-foreground">
                                        Sudah beredar
                                    </span>
                                    <span className="font-semibold text-foreground tabular-nums">
                                        {coin.supply_ratio_pct.toFixed(1)}%
                                    </span>
                                </div>
                                <div className="h-2 overflow-hidden rounded-full bg-muted">
                                    <div
                                        className="h-full bg-primary"
                                        style={{
                                            width: `${clamp(coin.supply_ratio_pct)}%`,
                                        }}
                                    />
                                </div>
                            </div>
                        )}
                    </Section>

                    <Section
                        title={t("insight.price_performance", "Performa harga")}
                    >
                        <div className="grid grid-cols-4 gap-2">
                            {(
                                [
                                    ["24 jam", coin.change["24h"]],
                                    ["7 hari", coin.change["7d"]],
                                    ["30 hari", coin.change["30d"]],
                                    ["1 tahun", coin.change["1y"]],
                                ] as const
                            ).map(([label, value]) => (
                                <div
                                    key={label}
                                    className="rounded-lg border border-border/60 bg-muted/30 p-2 text-center"
                                >
                                    <div className="text-[11px] text-muted-foreground">
                                        {label}
                                    </div>
                                    <div
                                        className={cn(
                                            "text-sm font-semibold tabular-nums",
                                            trendColor(value),
                                        )}
                                    >
                                        {orDash(value, (v) => formatPct(v, 1))}
                                    </div>
                                </div>
                            ))}
                        </div>
                    </Section>

                    <Section
                        title={t("insight.all_time_records", "Rekor harga")}
                    >
                        <div className="grid grid-cols-2 gap-2">
                            <Metric
                                label="All-time high"
                                hint="Harga tertinggi sepanjang masa."
                            >
                                {orDash(coin.ath.price, usd)}
                                <span className="mt-0.5 block text-xs font-normal text-muted-foreground">
                                    {formatDate(coin.ath.date, locale)}
                                </span>
                                <span
                                    className={cn(
                                        "block text-xs font-medium",
                                        trendColor(coin.ath.change_pct),
                                    )}
                                >
                                    {orDash(coin.ath.change_pct, (v) =>
                                        formatPct(v, 1),
                                    )}{" "}
                                    dari ATH
                                </span>
                            </Metric>
                            <Metric
                                label="All-time low"
                                hint="Harga terendah sepanjang masa."
                            >
                                {orDash(coin.atl.price, usd)}
                                <span className="mt-0.5 block text-xs font-normal text-muted-foreground">
                                    {formatDate(coin.atl.date, locale)}
                                </span>
                                <span
                                    className={cn(
                                        "block text-xs font-medium",
                                        trendColor(coin.atl.change_pct),
                                    )}
                                >
                                    {orDash(coin.atl.change_pct, (v) =>
                                        formatPct(v, 0),
                                    )}{" "}
                                    dari ATL
                                </span>
                            </Metric>
                        </div>
                    </Section>
                </>
            ) : (
                <Unavailable>
                    Data fundamental {name} dari CoinGecko belum tersedia.
                </Unavailable>
            )}

            <Section
                title={t("insight.global_crypto_market", "Pasar kripto global")}
            >
                {glob ? (
                    <div className="grid grid-cols-2 gap-2">
                        <Metric
                            label="Total market cap"
                            hint="Kapitalisasi pasar seluruh aset kripto."
                        >
                            {orDash(glob.total_market_cap, formatUsd)}
                            <span
                                className={cn(
                                    "ml-1 text-xs font-medium",
                                    trendColor(glob.market_cap_change_24h_pct),
                                )}
                            >
                                {orDash(glob.market_cap_change_24h_pct, (v) =>
                                    formatPct(v, 1),
                                )}
                            </span>
                        </Metric>
                        <Metric
                            label="Volume 24 jam"
                            hint="Total volume perdagangan seluruh kripto."
                        >
                            {orDash(glob.total_volume, formatUsd)}
                        </Metric>
                        <Metric
                            label="Dominasi BTC"
                            hint="Porsi Bitcoin dari total market cap."
                        >
                            {orDash(
                                glob.btc_dominance,
                                (v) => `${v.toFixed(1)}%`,
                            )}
                        </Metric>
                        <Metric
                            label="Dominasi ETH"
                            hint="Porsi Ethereum dari total market cap."
                        >
                            {orDash(
                                glob.eth_dominance,
                                (v) => `${v.toFixed(1)}%`,
                            )}
                        </Metric>
                    </div>
                ) : (
                    <Unavailable>Data pasar global belum tersedia.</Unavailable>
                )}

                {sentiment && (
                    <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                        <div className="mb-2 flex items-center justify-between text-xs">
                            <Hint text="Indeks Fear & Greed pasar kripto (alternative.me).">
                                <span className="text-muted-foreground">
                                    Fear &amp; Greed Index
                                </span>
                            </Hint>
                            {sentiment.yesterday !== null && (
                                <span className="text-muted-foreground tabular-nums">
                                    Kemarin {sentiment.yesterday}
                                </span>
                            )}
                        </div>
                        <div className="mb-3 flex items-baseline gap-2">
                            <span
                                className={cn(
                                    "text-2xl font-semibold tabular-nums",
                                    fngColor(sentiment.value),
                                )}
                            >
                                {sentiment.value}
                            </span>
                            <span
                                className={cn(
                                    "text-sm font-medium",
                                    fngColor(sentiment.value),
                                )}
                            >
                                {t(
                                    `insight.fng.${sentiment.classification.toLowerCase().replaceAll(" ", "_")}`,
                                    sentiment.classification,
                                )}
                            </span>
                        </div>
                        <div
                            className="relative h-2 rounded-full"
                            style={{
                                background:
                                    "linear-gradient(to right,#ef4444,#f97316,#eab308,#84cc16,#22c55e)",
                            }}
                            role="img"
                            aria-label={`Fear and Greed ${sentiment.value} dari 100`}
                        >
                            <div
                                className="absolute top-1/2 size-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background bg-foreground"
                                style={{ left: `${clamp(sentiment.value)}%` }}
                            />
                        </div>
                    </div>
                )}
            </Section>
        </div>
    );
}

/* ------------------------------- Global ---------------------------------- */

const DOMINANCE_SEGMENTS = [
    { key: "btc", label: "BTC", bar: "bg-orange-500" },
    { key: "eth", label: "ETH", bar: "bg-indigo-500 dark:bg-indigo-400" },
    { key: "usdt", label: "USDT", bar: "bg-emerald-500" },
    { key: "usdc", label: "USDC", bar: "bg-sky-500 dark:bg-sky-400" },
    { key: "others", label: "Lainnya", bar: "bg-muted-foreground" },
] as const;

function pegSignal(
    dev: number | null,
    t: (key: string, fallback?: string) => string,
): Signal | null {
    if (dev === null) return null;
    const abs = Math.abs(dev);
    if (abs < 0.3)
        return { text: t("insight.stable", "Stable"), className: GOOD };
    if (abs < 1)
        return {
            text: t("insight.slight_depeg", "Slight deviation"),
            className: WARN,
        };
    return { text: t("insight.depegged", "Depegged"), className: BAD };
}

function DominanceBar({
    data,
}: {
    data: NonNullable<GlobalData["dominance"]>;
}) {
    const parts = DOMINANCE_SEGMENTS.map((s) => ({
        ...s,
        value: data[s.key as keyof typeof data] as number | null,
    })).filter(
        (p): p is typeof p & { value: number } =>
            p.value !== null && p.value > 0,
    );

    if (parts.length === 0) return null;

    return (
        <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
            <div
                className="flex h-2.5 overflow-hidden rounded-full bg-muted"
                role="img"
                aria-label={parts
                    .map((p) => `${p.label} ${p.value.toFixed(1)}%`)
                    .join(", ")}
            >
                {parts.map((p) => (
                    <div
                        key={p.key}
                        className={p.bar}
                        style={{ width: `${p.value}%` }}
                    />
                ))}
            </div>
            <ul className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
                {parts.map((p) => (
                    <li key={p.key} className="flex items-center gap-1.5">
                        <span className={cn("size-2 rounded-full", p.bar)} />
                        {p.label}
                        <span className="text-foreground font-medium tabular-nums">
                            {p.value.toFixed(1)}%
                        </span>
                    </li>
                ))}
            </ul>
        </div>
    );
}

function StablecoinCard({ coin }: { coin: Stablecoin }) {
    const { t } = useI18n();
    const peg = pegSignal(coin.peg_deviation_pct, t);

    return (
        <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
            <div className="mb-2 flex items-center justify-between gap-2">
                <span className="text-sm font-semibold text-foreground">
                    {coin.code}{" "}
                    <span className="text-xs font-normal text-muted-foreground">
                        {coin.name}
                    </span>
                </span>
                <SignalBadge label="Peg" signal={peg} />
            </div>

            <div className="flex items-baseline justify-between gap-2">
                <span className="text-2xl font-semibold text-foreground tabular-nums">
                    {orDash(coin.market_cap, formatUsd)}
                </span>
                <span
                    className={cn(
                        "text-xs font-medium tabular-nums",
                        trendColor(coin.market_cap_change_24h_pct),
                    )}
                >
                    {orDash(coin.market_cap_change_24h_pct, (v) =>
                        formatPct(v),
                    )}
                    <span className="ml-1 text-muted-foreground">
                        {t("insight.24h", "24h")}
                    </span>
                </span>
            </div>

            <div className="mt-3 grid grid-cols-3 gap-2">
                <Metric
                    label="Dominasi"
                    hint={t(
                        "Porsi :code dari total market cap.",
                        "Porsi :code dari total market cap.",
                        { code: coin.code },
                    )}
                >
                    {orDash(coin.dominance_pct, (v) => `${v.toFixed(2)}%`)}
                </Metric>
                <Metric label="Harga" hint="Harga dalam USD.">
                    {orDash(coin.price, (v) => `$${v.toFixed(4)}`)}
                </Metric>
                <Metric label="Volume 24j" hint="Volume perdagangan 24 jam.">
                    {orDash(coin.volume_24h, formatUsd)}
                </Metric>
            </div>
        </div>
    );
}

function CommodityCard({ quote }: { quote: CommodityQuote }) {
    const { t } = useI18n();
    const {
        change,
        day_low: lo,
        day_high: hi,
        low_52w: lo52,
        high_52w: hi52,
    } = quote;
    const pos52 =
        lo52 !== null && hi52 !== null && hi52 > lo52
            ? clamp(((quote.price - lo52) / (hi52 - lo52)) * 100)
            : null;

    return (
        <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
            <div className="mb-1 flex items-center justify-between gap-2 text-xs text-muted-foreground">
                <span>
                    {quote.name}
                    {quote.label && (
                        <span className="ml-1 opacity-70">{quote.label}</span>
                    )}
                </span>
                <span>{quote.unit}</span>
            </div>

            <div className="flex items-baseline justify-between gap-2">
                <span className="text-2xl font-semibold text-foreground tabular-nums">
                    {usd(quote.price)}
                </span>
                <span
                    className={cn(
                        "text-sm font-medium tabular-nums",
                        trendColor(change["1d"]),
                    )}
                >
                    {orDash(change["1d"], (v) => formatPct(v))}
                    <span className="ml-1 text-xs text-muted-foreground">
                        harian
                    </span>
                </span>
            </div>

            <div className="mt-3 grid grid-cols-2 gap-2">
                {(
                    [
                        ["7 hari", change["7d"]],
                        ["30 hari", change["30d"]],
                    ] as const
                ).map(([label, value]) => (
                    <div
                        key={label}
                        className="rounded-lg border border-border/60 bg-muted/30 p-2 text-center"
                    >
                        <div className="text-[11px] text-muted-foreground">
                            {label}
                        </div>
                        <div
                            className={cn(
                                "text-sm font-semibold tabular-nums",
                                trendColor(value),
                            )}
                        >
                            {orDash(value, (v) => formatPct(v, 1))}
                        </div>
                    </div>
                ))}
            </div>

            {pos52 !== null && lo52 !== null && hi52 !== null && (
                <div className="mt-3">
                    <div className="relative h-1.5 rounded-full bg-muted">
                        <div
                            className="absolute top-1/2 size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background bg-primary"
                            style={{ left: `${pos52}%` }}
                        />
                    </div>
                    <div className="mt-2 flex justify-between text-xs text-muted-foreground tabular-nums">
                        <span>52 mgg {usd(lo52)}</span>
                        <span>{usd(hi52)}</span>
                    </div>
                </div>
            )}

            {lo !== null && hi !== null && (
                <div className="mt-2 text-xs text-muted-foreground tabular-nums">
                    Range hari ini {usd(lo)} – {usd(hi)}
                </div>
            )}

            <div className="mt-2 text-[11px] text-muted-foreground/80">
                {quote.source} · per{" "}
                {new Date(quote.as_of * 1000).toLocaleString("id-ID", {
                    day: "numeric",
                    month: "short",
                    hour: "2-digit",
                    minute: "2-digit",
                })}
            </div>
        </div>
    );
}

function GlobalPanel({ data }: { data: GlobalData }) {
    const { t } = useI18n();
    const { dominance: dom, stablecoins, commodities } = data;
    const stableList = stablecoins ? Object.values(stablecoins) : [];
    const stableShare =
        dom && dom.usdt !== null && dom.usdc !== null
            ? dom.usdt + dom.usdc
            : null;
    const commodityList = commodities ? Object.entries(commodities) : [];

    return (
        <div className="space-y-5">
            <Section
                title={t("insight.crypto_dominance", "Dominasi pasar kripto")}
            >
                {dom ? (
                    <>
                        <div className="grid grid-cols-2 gap-2">
                            <Metric
                                label="Dominasi BTC"
                                hint="Porsi Bitcoin dari total market cap."
                                className="text-orange-500 dark:text-orange-400"
                            >
                                {orDash(dom.btc, (v) => `${v.toFixed(2)}%`)}
                            </Metric>
                            <Metric
                                label="Dominasi ETH"
                                hint="Porsi Ethereum dari total market cap."
                                className="text-indigo-600 dark:text-indigo-300"
                            >
                                {orDash(dom.eth, (v) => `${v.toFixed(2)}%`)}
                            </Metric>
                        </div>
                        <DominanceBar data={dom} />
                        <div className="text-xs text-muted-foreground tabular-nums">
                            Total market cap{" "}
                            {orDash(dom.total_market_cap, formatUsd)}
                            {dom.market_cap_change_24h_pct !== null && (
                                <span
                                    className={cn(
                                        "ml-1 font-medium",
                                        trendColor(
                                            dom.market_cap_change_24h_pct,
                                        ),
                                    )}
                                >
                                    {formatPct(
                                        dom.market_cap_change_24h_pct,
                                        1,
                                    )}
                                </span>
                            )}
                        </div>
                    </>
                ) : (
                    <Unavailable>Data dominasi belum tersedia.</Unavailable>
                )}
            </Section>

            <Section title={t("insight.stablecoin", "Stablecoin")}>
                {stableList.length > 0 ? (
                    <>
                        {stableList.map((coin) => (
                            <StablecoinCard key={coin.code} coin={coin} />
                        ))}
                        {stableShare !== null && (
                            <p className="text-xs leading-relaxed text-muted-foreground">
                                USDT + USDC menguasai{" "}
                                <span className="text-foreground font-medium tabular-nums">
                                    {stableShare.toFixed(2)}%
                                </span>{" "}
                                pasar.
                            </p>
                        )}
                    </>
                ) : (
                    <Unavailable>Data stablecoin belum tersedia.</Unavailable>
                )}
            </Section>

            <Section title={t("insight.commodities", "Komoditas")}>
                {commodityList.length > 0 ? (
                    commodityList.map(([key, quote]) =>
                        quote ? (
                            <CommodityCard key={key} quote={quote} />
                        ) : (
                            <Unavailable key={key}>
                                Harga {key} belum tersedia.
                            </Unavailable>
                        ),
                    )
                ) : (
                    <Unavailable>Data komoditas belum tersedia.</Unavailable>
                )}
            </Section>
        </div>
    );
}

/* ------------------------------- Sinyal ---------------------------------- */

const RATING: Record<
    Rating,
    { spot: string; short: string; text: string; badge: string }
> = {
    strong_buy: {
        spot: "STRONG BUY",
        short: "Strong Buy",
        text: "text-emerald-600 dark:text-emerald-300",
        badge: GOOD,
    },
    buy: {
        spot: "BUY",
        short: "Buy",
        text: "text-emerald-500 dark:text-emerald-400",
        badge: GOOD,
    },
    neutral: {
        spot: "HOLD",
        short: "Netral",
        text: "text-foreground",
        badge: NEUTRAL,
    },
    sell: {
        spot: "SELL",
        short: "Sell",
        text: "text-red-500 dark:text-red-400",
        badge: BAD,
    },
    strong_sell: {
        spot: "STRONG SELL",
        short: "Strong Sell",
        text: "text-red-600 dark:text-red-300",
        badge: BAD,
    },
};

const TF_LONG: Record<string, string> = {
    "15m": "15 menit",
    "1h": "1 jam",
    "4h": "4 jam",
    "1d": "1 hari",
    "1w": "1 minggu",
};
const TF_SHORT: Record<string, string> = {
    "15m": "15m",
    "1h": "1J",
    "4h": "4J",
    "1d": "1H",
    "1w": "1M",
};

const VOTE_INFO: { key: SignalVote["key"]; label: string; hint: string }[] = [
    {
        key: "trend",
        label: "Trend",
        hint: "Gabungan EMA, EMA 200, DMI dan ADX; dinormalisasi terhadap ATR.",
    },
    {
        key: "momentum",
        label: "Momentum",
        hint: "Gabungan RSI, MACD histogram dan ROC, bukan voting biner.",
    },
    {
        key: "structure",
        label: "Market Structure",
        hint: "Breakout dan struktur harga dengan kontribusi terbatas dari SMC.",
    },
    {
        key: "volatility",
        label: "Volatility",
        hint: "Konteks Bollinger dan kekuatan trend untuk membedakan kondisi pasar.",
    },
    {
        key: "volume",
        label: "Volume",
        hint: "Konfirmasi arah berdasarkan volume relatif terhadap rata-rata 20 candle.",
    },
];

function VoteIcon({ vote }: { vote: SignalVote["vote"] }) {
    if (vote === 1)
        return (
            <ArrowUp
                className="size-4 text-emerald-500 dark:text-emerald-400"
                aria-hidden
            />
        );
    if (vote === -1)
        return (
            <ArrowDown
                className="size-4 text-red-500 dark:text-red-400"
                aria-hidden
            />
        );
    if (vote === 0)
        return <Minus className="size-4 text-muted-foreground" aria-hidden />;
    return (
        <span className="text-xs text-muted-foreground/60" aria-hidden>
            {DASH}
        </span>
    );
}

const voteWord = (v: SignalVote["vote"]) =>
    v === 1
        ? "bullish"
        : v === -1
          ? "bearish"
          : v === 0
            ? "netral"
            : "data belum cukup";

function ScoreGauge({ score }: { score: number }) {
    return (
        <div>
            <div
                className="relative h-2 rounded-full"
                style={{
                    background:
                        "linear-gradient(to right,#ef4444 0%,#f97316 25%,#6b7280 50%,#84cc16 75%,#22c55e 100%)",
                }}
                role="img"
                aria-label={`Skor gabungan ${score} dari rentang -100 sampai +100`}
            >
                <div
                    className="absolute top-1/2 size-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background bg-foreground"
                    style={{ left: `${clamp((score + 100) / 2)}%` }}
                />
            </div>
            <div className="mt-1 flex justify-between text-[10px] text-muted-foreground">
                <span>−100 Sell</span>
                <span>0</span>
                <span>Buy +100</span>
            </div>
        </div>
    );
}

function SignalPanel({
    data,
    signalMode,
    stale = false,
    workspaceSignal,
    workspaceSignalStatus,
    workspaceSignalDataIntegrity,
    workspaceSignalEnabled,
    workspaceSignalSummary,
}: {
    data: SignalData;
    signalMode: SignalMode;
    stale?: boolean;
    workspaceSignal: SignalSetup | null;
    workspaceSignalStatus: "idle" | "loading" | "success" | "empty" | "error";
    workspaceSignalDataIntegrity: boolean;
    workspaceSignalEnabled: boolean;
    workspaceSignalSummary: { label: string; detail: string };
}) {
    const { t } = useI18n();
    const { timeframes } = data;
    const [selectedTf, setSelectedTf] = useState<string>(data.primary.interval);

    useEffect(() => {
        setSelectedTf(data.primary.interval);
    }, [data.primary.interval]);

    const primaryFrame =
        timeframes.find((tf) => tf.interval === data.primary.interval) ?? null;
    const selectedFrame =
        selectedTf === "overall"
            ? null
            : (timeframes.find((tf) => tf.interval === selectedTf) ?? null);

    // Prefer the exact setup owned by Trading/index.tsx whenever its Signal
    // Engine is enabled. The sheet's fetch is a fallback for standalone use.
    const workspaceSignalReady =
        workspaceSignalEnabled &&
        workspaceSignalDataIntegrity &&
        workspaceSignal !== null;
    const signalSourceReady = !workspaceSignalEnabled || workspaceSignalReady;
    const setup = workspaceSignalEnabled
        ? workspaceSignalReady
            ? workspaceSignal
            : null
        : data.setup;
    // The API's canonical indicator diagnostics live under `primary`; setup
    // intentionally carries the executable plan and signal state. Resolve each
    // field independently so a partial primary payload cannot hide a fallback.
    const primaryRegimeRaw =
        data.primary.regime?.label?.trim() ||
        setup?.regime?.label?.trim() ||
        null;
    const primaryRegimeLabel = (() => {
        if (!primaryRegimeRaw)
            return t("Insufficient data", "Data belum cukup");

        const normalized = primaryRegimeRaw
            .trim()
            .toLowerCase()
            .replace(/[\s/-]+/g, "_");
        const labels: Record<string, string> = {
            bullish_strong: "Strong bullish",
            bullish_weak: "Weak bullish",
            bearish_strong: "Strong bearish",
            bearish_weak: "Weak bearish",
            bullish: "Bullish",
            bearish: "Bearish",
            range: "Range / Mixed",
            mixed: "Mixed",
            high_volatility: "High volatility",
            low_volatility: "Low volatility",
            bullish_trend: "Bullish Trend",
            bearish_trend: "Bearish Trend",
        };
        const label =
            labels[normalized] ?? primaryRegimeRaw.replaceAll("_", " ");
        return t(label, label);
    })();
    const primaryAdx =
        finiteMetric(data.primary.metrics?.adx) ??
        finiteMetric(setup?.metrics?.adx);
    const primaryAtrPct =
        finiteMetric(data.primary.metrics?.atr_pct) ??
        finiteMetric(setup?.metrics?.atr_pct);
    const primaryVolumeRatio =
        finiteMetric(data.primary.metrics?.volume_ratio) ??
        finiteMetric(setup?.metrics?.volume_ratio);
    const primaryScore = primaryFrame?.score ?? data.primary.score;
    const displayScore = selectedFrame?.score ?? primaryScore;
    const candidateDirection =
        setup?.candidate_direction === "long" ||
        setup?.candidate_direction === "short"
            ? setup.candidate_direction
            : setup?.candidate_action === "long" ||
                setup?.candidate_action === "short"
              ? setup.candidate_action
              : setup?.action === "long" || setup?.action === "short"
                ? setup.action
                : null;
    const signalState = signalSourceReady
        ? getSignalDisplayState(setup, data.market)
        : "neutral";
    const signalLabel = signalSourceReady
        ? `${getSignalDisplayLabel(signalState)}${!workspaceSignalEnabled && stale ? " · STALE" : ""}`
        : workspaceSignalSummary.label;
    const isConfirmed =
        signalSourceReady &&
        (signalState === "buy" ||
            signalState === "sell" ||
            signalState === "long" ||
            signalState === "short");
    const isCandidate =
        signalSourceReady &&
        (signalState === "buy_candidate" ||
            signalState === "sell_candidate" ||
            signalState === "long_candidate" ||
            signalState === "short_candidate");
    const signalTone =
        signalState === "buy" || signalState === "long"
            ? "text-emerald-600 dark:text-emerald-400"
            : signalState === "sell" || signalState === "short"
              ? "text-red-600 dark:text-red-400"
              : isCandidate
                ? "text-amber-600 dark:text-amber-400"
                : "text-foreground";
    const confidence =
        setup && Number.isFinite(setup.confidence)
            ? Math.max(0, Math.min(100, setup.confidence))
            : null;
    const mode = workspaceSignalEnabled
        ? signalMode
        : (setup?.signal_mode ??
          setup?.policy?.mode ??
          data.policy?.mode ??
          signalMode);
    const policy =
        setup?.policy ?? (workspaceSignalEnabled ? undefined : data.policy);
    const requirementGates = policy?.gates?.length
        ? policy.gates
        : candidateDirection
          ? (setup?.requirements?.[candidateDirection] ?? [])
          : [];
    const blockingReasons = Array.from(
        new Set(
            [
                ...(policy?.hard_failures ?? []),
                ...requirementGates
                    .filter((gate) => gate.hard && !gate.met)
                    .map((gate) => gate.label),
                ...(setup?.mtf?.blocking_reasons ?? []),
            ].filter(
                (reason): reason is string =>
                    typeof reason === "string" && reason.trim().length > 0,
            ),
        ),
    );
    const displayStale = !workspaceSignalEnabled && stale;
    const displayDetail =
        workspaceSignalEnabled && !workspaceSignalReady
            ? workspaceSignalSummary.detail
            : displayStale
              ? t(
                    "Pembaruan terakhir gagal; data sinyal di bawah ini adalah snapshot sebelumnya. Confidence :percent%.",
                    "Pembaruan terakhir gagal; data sinyal di bawah ini adalah snapshot sebelumnya. Confidence :percent%.",
                    { percent: Math.round(confidence ?? 0) },
                )
              : isConfirmed
                ? t("Confidence :percent%", "Confidence :percent%", {
                      percent: Math.round(confidence ?? 0),
                  })
                : isCandidate
                  ? t(
                        "Candidate only; execution requirements are not fully met. Confidence :percent%.",
                        "Candidate only; execution requirements are not fully met. Confidence :percent%.",
                        { percent: Math.round(confidence ?? 0) },
                    )
                  : t(
                        "Signal data is available, but no executable directional setup is active.",
                    );
    const statusLabel =
        workspaceSignalEnabled && !workspaceSignalReady
            ? workspaceSignalSummary.label.toUpperCase()
            : displayStale
              ? "EXPIRED DATA"
              : isConfirmed
                ? "CONFIRMED"
                : isCandidate
                  ? "CANDIDATE"
                  : "NEUTRAL";
    const statusVariant =
        workspaceSignalEnabled && !workspaceSignalReady
            ? workspaceSignalStatus === "error"
                ? "destructive"
                : "secondary"
            : displayStale
              ? "destructive"
              : isConfirmed
                ? "default"
                : isCandidate
                  ? "outline"
                  : "secondary";
    const cols = `minmax(0,1fr) repeat(${timeframes.length}, 3.25rem)`;

    return (
        <div className="space-y-5">
            <div
                className="flex flex-wrap gap-1 rounded-lg bg-muted/60 p-1 border border-border/50"
                role="group"
                aria-label="Pilih timeframe sinyal"
            >
                <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    onClick={() => setSelectedTf(data.primary.interval)}
                    aria-pressed={selectedTf === data.primary.interval}
                    className={cn(
                        "h-7 flex-1 rounded-md text-xs font-medium transition-colors shadow-none px-3",
                        selectedTf === data.primary.interval
                            ? "bg-primary text-primary-foreground hover:bg-primary/90"
                            : "text-muted-foreground hover:bg-background hover:text-foreground",
                    )}
                >
                    Utama ·{" "}
                    {TF_SHORT[data.primary.interval] ?? data.primary.interval}
                </Button>
                {timeframes.map((tf) => (
                    <Button
                        key={tf.interval}
                        type="button"
                        variant="ghost"
                        size="sm"
                        onClick={() => setSelectedTf(tf.interval)}
                        aria-pressed={selectedTf === tf.interval}
                        title={TF_LONG[tf.interval] ?? tf.interval}
                        className={cn(
                            "h-7 flex-1 rounded-md text-xs font-medium transition-colors shadow-none px-3",
                            selectedTf === tf.interval
                                ? "bg-primary text-primary-foreground hover:bg-primary/90"
                                : "text-muted-foreground hover:bg-background hover:text-foreground",
                        )}
                    >
                        {TF_SHORT[tf.interval] ?? tf.interval}
                    </Button>
                ))}
            </div>

            <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                <div className="flex flex-wrap items-start justify-between gap-3">
                    <div className="min-w-0">
                        <div className="mb-1 text-xs text-muted-foreground">
                            {data.market === "spot"
                                ? "Signal Spot"
                                : "Signal Futures"}
                            {" · "}
                            {TF_LONG[data.primary.interval] ??
                                data.primary.interval}{" "}
                            · utama
                        </div>
                        <div
                            className={cn(
                                "text-xl font-bold tracking-tight",
                                signalTone,
                            )}
                        >
                            {signalLabel}
                        </div>
                        <div className="mt-1 text-xs text-muted-foreground">
                            {displayDetail}
                        </div>
                    </div>
                    <div className="flex flex-wrap items-center gap-1.5">
                        <Badge variant={statusVariant}>{statusLabel}</Badge>
                        <Badge variant="outline" className="uppercase">
                            {mode}
                        </Badge>
                    </div>
                </div>
                <div className="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-4">
                    <Metric label="Confidence">
                        {confidence === null
                            ? DASH
                            : `${Math.round(confidence)}%`}
                    </Metric>
                    <Metric label="Skor utama">
                        {signalSourceReady
                            ? `${data.primary.score > 0 ? "+" : ""}${data.primary.score.toFixed(1)}`
                            : DASH}
                    </Metric>
                    <Metric label="Entry">
                        {signalSourceReady &&
                        !displayStale &&
                        isConfirmed &&
                        setup?.entry != null
                            ? formatPrice(setup?.entry)
                            : DASH}
                    </Metric>
                    <Metric label="Risk / Reward">
                        {signalSourceReady &&
                        !displayStale &&
                        isConfirmed &&
                        setup?.risk_reward?.ratio != null
                            ? `${setup.risk_reward.ratio.toFixed(2)}R`
                            : DASH}
                    </Metric>
                </div>
                {signalSourceReady && !displayStale && isConfirmed && (
                    <div className="mt-2 grid grid-cols-2 gap-2">
                        <Metric label="Stop Loss">
                            {setup?.sl == null ? DASH : formatPrice(setup.sl)}
                        </Metric>
                        <Metric label="Take Profit">
                            {setup?.tp == null ? DASH : formatPrice(setup.tp)}
                        </Metric>
                    </div>
                )}
                {signalSourceReady &&
                    !displayStale &&
                    !isConfirmed &&
                    blockingReasons.length > 0 && (
                        <div className="mt-3 rounded-lg border border-amber-500/20 bg-amber-500/5 p-3 text-xs text-amber-700 dark:text-amber-300">
                            <div className="mb-1 font-semibold">
                                Alasan belum lolos
                            </div>
                            <ul className="list-disc space-y-1 pl-4">
                                {blockingReasons.map((reason, index) => (
                                    <li key={`${index}-${reason}`}>{reason}</li>
                                ))}
                            </ul>
                        </div>
                    )}
            </div>

            <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                <div className="mb-2 text-xs text-muted-foreground">
                    {selectedFrame
                        ? `Skor diagnostik · ${TF_LONG[selectedFrame.interval] ?? selectedFrame.interval}`
                        : `Skor timeframe utama · ${TF_LONG[data.primary.interval] ?? data.primary.interval}`}
                    {selectedFrame &&
                    selectedFrame.interval !== data.primary.interval
                        ? " · tidak mengubah arah sinyal utama"
                        : ""}
                </div>
                <ScoreGauge score={displayScore} />
            </div>

            <div className="grid gap-2 sm:grid-cols-4">
                <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                    <div className="text-[10px] uppercase tracking-[.12em] text-muted-foreground">
                        {t("Regime", "Regime")}
                    </div>
                    <div className="mt-1 text-sm font-semibold">
                        {primaryRegimeLabel}
                    </div>
                </div>
                <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                    <div className="text-[10px] uppercase tracking-[.12em] text-muted-foreground">
                        ADX
                    </div>
                    <div className="mt-1 text-sm font-semibold tabular-nums">
                        {primaryAdx === null
                            ? "Data belum cukup"
                            : primaryAdx.toFixed(1)}
                    </div>
                </div>
                <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                    <div className="text-[10px] uppercase tracking-[.12em] text-muted-foreground">
                        ATR
                    </div>
                    <div className="mt-1 text-sm font-semibold tabular-nums">
                        {primaryAtrPct === null
                            ? "Data belum cukup"
                            : `${primaryAtrPct.toFixed(2)}%`}
                    </div>
                </div>
                <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                    <div className="text-[10px] uppercase tracking-[.12em] text-muted-foreground">
                        Volume Ratio
                    </div>
                    <div className="mt-1 text-sm font-semibold tabular-nums">
                        {primaryVolumeRatio === null
                            ? "Data belum cukup"
                            : `${primaryVolumeRatio.toFixed(2)}×`}
                    </div>
                </div>
            </div>

            <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                <div className="flex flex-wrap items-center gap-2">
                    <div className="text-xs font-semibold">
                        {t("signal.lifecycle", "Signal lifecycle")}
                    </div>
                    <Badge variant="outline" className="text-[10px]">
                        {setup?.lifecycle?.status === "active"
                            ? t("signal.active", "ACTIVE")
                            : setup?.lifecycle?.status === "target_hit"
                              ? t("signal.target_hit", "TARGET HIT")
                              : setup?.lifecycle?.status === "invalidated"
                                ? t("signal.invalidated", "INVALIDATED")
                                : t("signal.inactive", "INACTIVE")}
                    </Badge>
                    <span className="ml-auto text-[10px] text-muted-foreground">
                        {setup?.lifecycle?.transitions.length ?? 0}{" "}
                        {t("signal.lifecycle_events", "event(s)")}
                    </span>
                </div>
                <div className="mt-2 flex flex-wrap gap-2 text-[10px] text-muted-foreground">
                    {(setup?.lifecycle?.transitions ?? []).map(
                        (event, index) => (
                            <span
                                key={`${event.at}-${index}`}
                                className="rounded-full border border-border px-2 py-1"
                            >
                                {event.status.toUpperCase()} ·{" "}
                                {new Date(event.at * 1000).toLocaleTimeString(
                                    [],
                                    {
                                        hour: "2-digit",
                                        minute: "2-digit",
                                    },
                                )}
                            </span>
                        ),
                    )}
                </div>
            </div>

            <Section
                title={t(
                    "insight.by_timeframe",
                    "Per timeframe (klik untuk fokus)",
                )}
            >
                <div className="grid grid-cols-3 gap-2">
                    {timeframes.map((tf) => (
                        <button
                            key={tf.interval}
                            type="button"
                            onClick={() =>
                                setSelectedTf((prev) =>
                                    prev === tf.interval
                                        ? "overall"
                                        : tf.interval,
                                )
                            }
                            aria-pressed={selectedTf === tf.interval}
                            className={cn(
                                "rounded-lg border p-2 text-center transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
                                selectedTf === tf.interval
                                    ? "border-primary bg-primary/10 shadow-sm"
                                    : "border-border/60 bg-muted/30 hover:border-border hover:bg-muted/50",
                            )}
                        >
                            <div className="text-[11px] text-muted-foreground">
                                {TF_LONG[tf.interval] ?? tf.interval}
                            </div>
                            <Badge
                                variant="outline"
                                className={cn(
                                    "my-1 px-1.5 py-0 text-[10px]",
                                    RATING[tf.rating].badge,
                                )}
                            >
                                {RATING[tf.rating].short}
                            </Badge>
                            <div className="text-xs text-muted-foreground tabular-nums">
                                {tf.score > 0 ? "+" : ""}
                                {tf.score.toFixed(1)} · bobot{" "}
                                {Math.round(tf.weight * 100)}%
                            </div>
                        </button>
                    ))}
                </div>
            </Section>

            <Section
                title={t(
                    "insight.signal_reasons",
                    "Alasan sinyal (suara tiap indikator)",
                )}
            >
                <div className="rounded-lg border border-border/60 bg-muted/30 p-3">
                    <div
                        className="grid items-center gap-y-1"
                        style={{ gridTemplateColumns: cols }}
                    >
                        <div />
                        {timeframes.map((tf) => (
                            <div
                                key={tf.interval}
                                className={cn(
                                    "pb-1 text-center text-[11px] font-medium",
                                    selectedTf === tf.interval
                                        ? "text-primary font-semibold"
                                        : "text-muted-foreground",
                                )}
                            >
                                {TF_SHORT[tf.interval] ?? tf.interval}
                            </div>
                        ))}

                        {VOTE_INFO.map(({ key, label, hint }) => (
                            <div key={key} className="contents">
                                <div className="py-1.5 pr-2 text-xs text-foreground">
                                    <Hint text={hint}>{label}</Hint>
                                </div>
                                {timeframes.map((tf) => {
                                    const v = tf.votes.find(
                                        (x) => x.key === key,
                                    );
                                    const vote = v?.vote ?? null;

                                    return (
                                        <div
                                            key={tf.interval}
                                            className="flex justify-center"
                                        >
                                            <Tooltip>
                                                <TooltipTrigger asChild>
                                                    <button
                                                        type="button"
                                                        aria-label={`${label} ${TF_LONG[tf.interval] ?? tf.interval}: ${voteWord(vote)}. ${v?.detail ?? ""}`}
                                                        className="flex size-7 items-center justify-center rounded-md hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
                                                    >
                                                        <VoteIcon vote={vote} />
                                                    </button>
                                                </TooltipTrigger>
                                                <TooltipContent
                                                    side="top"
                                                    className="max-w-[16rem]"
                                                >
                                                    {v?.detail ??
                                                        "Data belum cukup"}
                                                </TooltipContent>
                                            </Tooltip>
                                        </div>
                                    );
                                })}
                            </div>
                        ))}
                    </div>
                </div>
            </Section>
        </div>
    );
}

/* ------------------------------- Sheet utama ------------------------------ */

const tabItemClass =
    "h-8 flex-1 rounded-md text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground data-[state=on]:bg-primary data-[state=on]:text-primary-foreground transition-colors";

export default function CoinInsightSheet({
    symbol,
    market,
    name,
    interval,
    provider,
    signalMode,
    workspaceSignal,
    workspaceSignalStatus,
    workspaceSignalDataIntegrity,
    workspaceSignalEnabled,
    workspaceSignalSummary,
}: CoinInsightSheetProps) {
    const { t, locale } = useI18n();
    const [open, setOpen] = useState(false);
    const [tab, setTab] = useState<Tab>("micro");

    const base = symbol.replace(/USDT$/, "");
    const query = `symbol=${encodeURIComponent(symbol)}&market=${market}&interval=${encodeURIComponent(interval)}&provider=${encodeURIComponent(provider)}`;
    const signalQuery = `${query}&signal_mode=${encodeURIComponent(signalMode)}&include_backtest=0`;

    const micro = usePolledJson<MicroData>(
        `/market/micro?${query}`,
        MICRO_REFRESH_MS,
        open && tab === "micro",
    );
    const macro = usePolledJson<MacroData>(
        `/market/macro?${query}`,
        MACRO_REFRESH_MS,
        open && tab === "macro",
    );

    const signal = usePolledJson<SignalData>(
        `/market/signals?${signalQuery}`,
        SIGNAL_REFRESH_MS,
        open && tab === "signal",
    );

    const globalData = usePolledJson<GlobalData>(
        "/market/global",
        GLOBAL_REFRESH_MS,
        open && tab === "global",
    );

    const signalDataMatchesContext = Boolean(
        signal.data &&
        signal.data.symbol === symbol &&
        signal.data.market === market &&
        signal.data.primary.interval === interval &&
        signal.data.setup.interval === interval &&
        (signal.data.setup.signal_mode ??
            signal.data.setup.policy?.mode ??
            signal.data.policy?.mode) === signalMode,
    );

    const active =
        tab === "micro"
            ? micro
            : tab === "macro"
              ? macro
              : tab === "signal"
                ? signal
                : globalData;

    return (
        <Sheet open={open} onOpenChange={setOpen}>
            <SheetTrigger asChild>
                <Button
                    variant="outline"
                    size="sm"
                    className="gap-1.5 border-border bg-background text-foreground hover:bg-muted hover:text-foreground"
                >
                    <Activity className="size-4 text-primary" />
                    {t("insight.micro_macro", "Micro & Macro")}
                </Button>
            </SheetTrigger>

            <SheetContent
                side="right"
                className="w-full gap-0 border-border bg-popover p-0 text-popover-foreground sm:max-w-md flex flex-col"
            >
                <SheetHeader className="border-b border-border p-4">
                    <SheetTitle className="text-foreground text-left">
                        {name}{" "}
                        <span className="text-sm font-normal text-muted-foreground">
                            {base}/USDT
                            {market === "futures" ? (
                                <> · {t("insight.futures", "Futures")}</>
                            ) : null}
                        </span>
                    </SheetTitle>
                    <SheetDescription className="text-muted-foreground text-left text-xs">
                        <span className="inline-flex flex-wrap items-center gap-1.5">
                            <span>{provider.toUpperCase()}</span>
                            <span aria-hidden="true">·</span>
                            <span>{market}</span>
                            <span aria-hidden="true">·</span>
                            <span>{interval}</span>
                            {tab === "signal" ? (
                                <>
                                    <span aria-hidden="true">·</span>
                                    <span className="font-medium text-foreground">
                                        {signalMode}
                                    </span>
                                </>
                            ) : null}
                        </span>
                        <span className="mt-1 block">
                            {tab === "global"
                                ? t(
                                      "insight.global_description",
                                      "Crypto dominance, stablecoins, and global commodities.",
                                  )
                                : t(
                                      "insight.description",
                                      "Micro, macro, and technical signal data.",
                                  )}
                        </span>
                    </SheetDescription>

                    <ToggleGroup
                        type="single"
                        value={tab}
                        onValueChange={(v) => {
                            if (v) setTab(v as Tab);
                        }}
                        className="mt-3 w-full gap-1 rounded-lg bg-muted/60 p-1 border border-border/50"
                        aria-label={t("insight.data_type", "Data type")}
                    >
                        <ToggleGroupItem value="micro" className={tabItemClass}>
                            {t("Micro")}
                        </ToggleGroupItem>
                        <ToggleGroupItem value="macro" className={tabItemClass}>
                            {t("Macro")}
                        </ToggleGroupItem>
                        <ToggleGroupItem
                            value="signal"
                            className={tabItemClass}
                        >
                            {t("Signal")}
                        </ToggleGroupItem>
                        <ToggleGroupItem
                            value="global"
                            className={tabItemClass}
                        >
                            Global
                        </ToggleGroupItem>
                    </ToggleGroup>
                </SheetHeader>

                <div className="flex-1 space-y-4 overflow-y-auto px-4 py-4">
                    {active.error && (
                        <ErrorNote
                            message={active.error}
                            hasData={active.data !== null}
                            onRetry={active.refetch}
                            retrying={active.loading}
                        />
                    )}

                    {tab === "micro" &&
                        (micro.data ? (
                            <MicroPanel data={micro.data} base={base} />
                        ) : (
                            !micro.error && <PanelSkeleton />
                        ))}

                    {tab === "macro" &&
                        (macro.data ? (
                            <MacroPanel data={macro.data} name={name} />
                        ) : (
                            !macro.error && <PanelSkeleton />
                        ))}

                    {tab === "signal" &&
                        (signal.data ? (
                            signalDataMatchesContext ? (
                                <SignalPanel
                                    data={signal.data}
                                    signalMode={signalMode}
                                    stale={Boolean(signal.error)}
                                    workspaceSignal={workspaceSignal}
                                    workspaceSignalStatus={
                                        workspaceSignalStatus
                                    }
                                    workspaceSignalDataIntegrity={
                                        workspaceSignalDataIntegrity
                                    }
                                    workspaceSignalEnabled={
                                        workspaceSignalEnabled
                                    }
                                    workspaceSignalSummary={
                                        workspaceSignalSummary
                                    }
                                />
                            ) : (
                                <Unavailable>
                                    Memverifikasi konteks sinyal agar sesuai
                                    dengan simbol, market, timeframe, dan mode
                                    aktif…
                                </Unavailable>
                            )
                        ) : (
                            !signal.error && <PanelSkeleton />
                        ))}

                    {tab === "global" &&
                        (globalData.data ? (
                            <GlobalPanel data={globalData.data} />
                        ) : (
                            !globalData.error && <PanelSkeleton />
                        ))}
                </div>

                <SheetFooter className="border-t border-border p-3 text-[11px] leading-relaxed text-muted-foreground">
                    <p>
                        {tab === "micro"
                            ? t(
                                  "insight.source_binance",
                                  "Source: Binance Spot. Updated every :seconds seconds",
                                  {
                                      seconds: MICRO_REFRESH_MS / 1000,
                                  },
                              )
                            : tab === "macro"
                              ? t(
                                    "insight.source_coingecko",
                                    "Source: CoinGecko. Updated every :seconds seconds",
                                    {
                                        seconds: MACRO_REFRESH_MS / 1000,
                                    },
                                )
                              : tab === "signal"
                                ? t(
                                      "insight.signal_engine_context",
                                      "Signal Engine · :market · :mode · :interval candles.",
                                      {
                                          market,
                                          mode: signalMode,
                                          interval,
                                      },
                                  )
                                : t(
                                      "insight.source_global",
                                      "Integrated global sources.",
                                  )}
                        {active.updatedAt &&
                            t("insight.last_updated", " · last updated :time", {
                                time: new Date(
                                    active.updatedAt,
                                ).toLocaleTimeString(
                                    locale === "id" ? "id-ID" : "en-US",
                                ),
                            })}
                        .
                    </p>
                </SheetFooter>
            </SheetContent>
        </Sheet>
    );
}
