import { Head, router, usePage } from "@inertiajs/react";
import type { PageProps } from "@inertiajs/core";
import type { ReactNode } from "react";
import {
    lazy,
    Suspense,
    useCallback,
    useEffect,
    useMemo,
    useRef,
    useState,
    useTransition,
} from "react";
const CoinInsightSheet = lazy(
    () => import("@/components/trading/CoinInsightSheet"),
);
import CustomChart from "@/components/trading/CustomChart";
const BacktestPanel = lazy(() => import("@/components/trading/BacktestPanel"));
const SignalEngineReplayPanel = lazy(
    () => import("@/components/trading/SignalEngineReplayPanel"),
);
const BacktestChartOverlay = lazy(
    () => import("@/components/trading/BacktestChartOverlay"),
);
import type { BacktestChartHandle } from "@/components/trading/BacktestChartOverlay";
const SignalEngine = lazy(() => import("@/components/trading/SignalEngine"));
const SignalCalibrationPanel = lazy(
    () => import("@/components/trading/SignalCalibrationPanel"),
);
const TradePlan = lazy(() => import("@/components/trading/TradePlan"));
const RegimeDetectionPanel = lazy(
    () => import("@/components/trading/RegimeDetectionPanel"),
);
const MarketBreadthPanel = lazy(
    () => import("@/components/trading/MarketBreadthPanel"),
);
const MarketRelayPanel = lazy(
    () => import("@/components/trading/MarketRelayPanel"),
);
import type { PatternEvent, SignalMode, SignalSetup } from "@/types/insight";
const SignalHistory = lazy(() => import("@/components/trading/SignalHistory"));
const SignalPerformance = lazy(
    () => import("@/components/trading/SignalPerformance"),
);
import SignalEventMarkerOverlay, {
    type SignalAlertEvent,
} from "@/components/trading/SignalEventMarkerOverlay";
const SignalChartOverlay = lazy(
    () => import("@/components/trading/SignalChartOverlay"),
);
import type { BacktestResult } from "@/lib/backtest-types";
import TradingChartToolbar from "@/components/trading/TradingChartToolbar";
import PatternChartOverlay from "@/components/trading/PatternChartOverlay";
import {
    ContextMenu,
    ContextMenuCheckboxItem,
    ContextMenuContent,
    ContextMenuLabel,
    ContextMenuSeparator,
    ContextMenuSub,
    ContextMenuSubContent,
    ContextMenuSubTrigger,
    ContextMenuItem,
    ContextMenuTrigger,
} from "@/components/ui/context-menu";
import TradingOnboarding from "@/components/trading/TradingOnboarding";
import AlertCenter, {
    type TradingAlert,
} from "@/components/trading/AlertCenter";
import SubscriptionGate, {
    type TradingSubscription,
} from "@/components/trading/SubscriptionGate";
import SubscriptionLifecycleBanner from "@/components/trading/SubscriptionLifecycleBanner";
import { isMarketTicker } from "@/lib/market-api";
import { useActiveMarket } from "@/components/trading/active-market";
import { useI18n } from "@/lib/i18n";
import { hasSubscriptionFeature } from "@/lib/subscription-access";
import {
    DEFAULT_INDICATORS,
    formatPrice,
    type IndicatorState,
    type MarketType,
} from "@/lib/market";
const CorrelationPanel = lazy(
    () => import("@/components/trading/CorrelationPanel"),
);
const PortfolioRiskPanel = lazy(
    () => import("@/components/trading/PortfolioRiskPanel"),
);
const VolumeProfilePanel = lazy(
    () => import("@/components/trading/VolumeProfilePanel"),
);
import type { VolumeProfileResult } from "@/lib/volume-profile";
import {
    createSignalSnapshot,
    getSignalDisplayLabel,
    getSignalDisplayState,
    isExecutableSignal,
    isSignalSnapshotCurrent,
    type SignalSnapshot,
} from "@/lib/SignalSnapshot";
import {
    TrendingUp,
    TrendingDown,
    Activity,
    Zap,
    HelpCircle,
    Sparkles,
    BarChart3,
    Eraser,
    RotateCcw,
    RefreshCw,
} from "lucide-react";
import AppLayout from "@/layouts/app-layout";
import { Badge } from "@/components/ui/badge";
import AnalysisWidgetSettings, { type WorkspacePreset } from "@/components/analysis-widget-settings";
import MarketAnalysisSnapshot from "@/components/trading/MarketAnalysisSnapshot";

interface SymbolItem {
    symbol: string;
    name: string;
}

interface Ticker {
    symbol: string;
    last: number;
    change_pct: number;
    quote_volume: number;
}

type TickerSnapshot = {
    context: string;
    values: Record<string, Ticker>;
};

type LiveQuoteSnapshot = {
    context: string;
    price: number | null;
    candleTime: number | null;
};

type ChartDataSnapshot = {
    context: string;
    candleTimes: number[];
};

type VolumeProfileSnapshot = {
    context: string;
    profile: VolumeProfileResult | null;
};

interface SharedProps extends PageProps {
    features?: {
        backtest_enabled?: boolean;
        signal_engine_enabled?: boolean;
    };
    tradingCards?: Record<string, boolean>;
    subscription?: TradingSubscription | null;
}

const CHART_OVERLAY_KEYS = Object.keys(
    DEFAULT_INDICATORS,
) as (keyof IndicatorState)[];

interface IndexTradingProps {
    symbol: string;
    interval: string;
    market: MarketType;
    symbols: Record<MarketType, SymbolItem[]>;
    intervals: string[];
    wsUrl: string | null;
    wsDriver?: string;
    provider?: string;
}

function normalizeIndicators(value: unknown): IndicatorState {
    const result: IndicatorState = { ...DEFAULT_INDICATORS };

    if (!value || typeof value !== "object") return result;

    const source = value as Record<string, unknown>;
    (Object.keys(DEFAULT_INDICATORS) as (keyof IndicatorState)[]).forEach(
        (key) => {
            if (typeof source[key] === "boolean") {
                result[key] = source[key] as IndicatorState[typeof key];
            }
        },
    );

    return result;
}

function isRecord(value: unknown): value is Record<string, unknown> {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}

function isPatternEvent(value: unknown): value is PatternEvent {
    if (!isRecord(value) || !isRecord(value.meta)) return false;

    const validMeta = Object.values(value.meta).every(
        (item) =>
            item === null ||
            typeof item === "string" ||
            (typeof item === "number" && Number.isFinite(item)),
    );

    return (
        (value.type === "breakout" ||
            value.type === "pullback" ||
            value.type === "reversal") &&
        (value.direction === "long" || value.direction === "short") &&
        typeof value.time === "number" &&
        Number.isInteger(value.time) &&
        value.time > 0 &&
        typeof value.price === "number" &&
        Number.isFinite(value.price) &&
        value.price > 0 &&
        typeof value.quality === "number" &&
        Number.isFinite(value.quality) &&
        value.quality >= 0 &&
        value.quality <= 100 &&
        (value.grade === "A+" ||
            value.grade === "A" ||
            value.grade === "B" ||
            value.grade === "C") &&
        validMeta
    );
}

type SubscriptionPayload = {
    plan: string;
    name: string;
    features: string[];
    active: boolean;
    ends_at: string | null;
    is_trial: boolean;
};

function isSubscriptionPayload(value: unknown): value is SubscriptionPayload {
    if (!isRecord(value)) return false;

    const endsAtIsValid =
        value.ends_at === null ||
        (typeof value.ends_at === "string" &&
            Number.isFinite(Date.parse(value.ends_at)));

    return (
        typeof value.plan === "string" &&
        value.plan.trim().length > 0 &&
        typeof value.name === "string" &&
        value.name.trim().length > 0 &&
        Array.isArray(value.features) &&
        value.features.every((feature) => typeof feature === "string") &&
        typeof value.active === "boolean" &&
        endsAtIsValid &&
        typeof value.is_trial === "boolean"
    );
}

function getProviderDisplayName(provider: string): string {
    const normalized = provider.trim().toLowerCase();
    const knownNames: Record<string, string> = {
        binance: "Binance",
        bybit: "Bybit",
        okx: "OKX",
    };

    if (knownNames[normalized]) return knownNames[normalized];

    return (
        normalized
            .replace(/[-_]+/g, " ")
            .replace(/\b[a-z]/g, (letter) => letter.toUpperCase()) || "Unknown"
    );
}

type TradingHorizon = "scalping" | "intraday" | "swing" | "position";

const TRADING_HORIZON_OPTIONS: Array<{
    id: TradingHorizon;
    label: string;
    description: string;
    intervals: string[];
}> = [
    {
        id: "scalping",
        label: "Scalping",
        description: "1m–5m · A very short-term setup.",
        intervals: ["1m", "5m"],
    },
    {
        id: "intraday",
        label: "Intraday",
        description: "15m–1h · A short-term analysis within a trading session.",
        intervals: ["15m", "30m", "1h"],
    },
    {
        id: "swing",
        label: "Swing",
        description:
            "4h–1D · A medium-term analysis focusing on trends and structure.",
        intervals: ["4h", "1d"],
    },
    {
        id: "position",
        label: "Position",
        description:
            "1W · A long-term analysis focusing on overall market trends.",
        intervals: ["1w"],
    },
];

function getTradingHorizon(interval: string): TradingHorizon {
    const normalized = interval.trim().toLowerCase();
    return (
        TRADING_HORIZON_OPTIONS.find((option) =>
            option.intervals.includes(normalized),
        )?.id ?? "intraday"
    );
}

function getTradingHorizonOption(horizon: TradingHorizon) {
    return (
        TRADING_HORIZON_OPTIONS.find((option) => option.id === horizon) ??
        TRADING_HORIZON_OPTIONS[1]
    );
}

function getPatternRefreshDelay(interval: string): number {
    const match = /^(\d+)(m|h|d|w)$/i.exec(interval.trim());
    if (!match) return 60_000;

    const amount = Number(match[1]);
    const unit = match[2].toLowerCase();
    const unitMilliseconds: Record<string, number> = {
        m: 60_000,
        h: 3_600_000,
        d: 86_400_000,
        w: 604_800_000,
    };
    const candleMilliseconds = amount * unitMilliseconds[unit];
    if (!Number.isFinite(candleMilliseconds) || candleMilliseconds <= 0) {
        return 60_000;
    }

    // PatternDetectionService evaluates closed candles, so refresh just after
    // the next timeframe boundary instead of repeatedly analyzing the same bar.
    // Crypto weekly candles conventionally open on Monday UTC; Unix epoch
    // alignment would otherwise place the weekly refresh on Thursday.
    const alignmentOffset = unit === "w" ? 4 * unitMilliseconds.d : 0;
    const elapsed = (Date.now() - alignmentOffset) % candleMilliseconds;
    const untilClose = candleMilliseconds - elapsed;
    return Math.max(5_000, untilClose + 2_000);
}

const pair = (symbol: string) => symbol.replace(/USDT$/, "/USDT");


type DecisionClarityRailProps = {
    signalStatus: "idle" | "loading" | "success" | "empty" | "error";
    signalDisplayState: string;
    signalDataIntegrity: boolean;
    signal: SignalSetup | null;
    price: number | null;
};

const DecisionClarityRail = ({
    signalStatus,
    signalDisplayState,
    signalDataIntegrity,
    signal,
    price,
}: DecisionClarityRailProps) => {
    const loading = signalStatus === "loading";
    const stale = signalStatus === "error";
    const candidate =
        signalDisplayState === "buy_candidate" ||
        signalDisplayState === "sell_candidate" ||
        signalDisplayState === "long_candidate" ||
        signalDisplayState === "short_candidate";
    const confirmed =
        signalDataIntegrity &&
        !!signal &&
        isExecutableSignal(signal);
    const hasDirectionalSetup =
        confirmed ||
        candidate ||
        signalDisplayState === "buy" ||
        signalDisplayState === "sell" ||
        signalDisplayState === "long" ||
        signalDisplayState === "short";

    const steps = [
        {
            label: "Market context",
            state: loading
                ? "Reviewing"
                : price != null
                  ? "Available"
                  : "Unavailable",
            tone:
                price != null
                    ? "border-info/20 bg-info/5 text-info"
                    : "border-border/60 bg-muted/20 text-muted-foreground",
        },
        {
            label: "Signal candidate",
            state: candidate || confirmed ? "Detected" : "None",
            tone:
                candidate || confirmed
                    ? "border-warning/25 bg-warning/5 text-warning-foreground"
                    : "border-border/60 bg-muted/20 text-muted-foreground",
        },
        {
            label: "Confirmation",
            state: stale
                ? "Stale"
                : loading
                  ? "Calculating"
                  : confirmed
                    ? "Confirmed"
                    : candidate
                      ? "Pending"
                      : "Blocked",
            tone:
                stale
                    ? "border-destructive/25 bg-destructive/5 text-destructive"
                    : confirmed
                      ? "border-success/25 bg-success/5 text-success"
                      : candidate
                        ? "border-warning/25 bg-warning/5 text-warning-foreground"
                        : "border-border/60 bg-muted/20 text-muted-foreground",
        },
        {
            label: "Plan & risk",
            state: confirmed ? "Ready" : hasDirectionalSetup ? "Review" : "Blocked",
            tone:
                confirmed
                    ? "border-success/25 bg-success/5 text-success"
                    : "border-border/60 bg-muted/20 text-muted-foreground",
        },
    ];

    return (
        <div
            className="mt-3 rounded-xl border border-border/60 bg-muted/10 p-2.5 sm:p-3"
            aria-label="Decision clarity"
        >
            <div className="mb-2 flex items-center justify-between gap-3">
                <div>
                    <p className="text-[9px] font-bold uppercase tracking-[0.16em] text-muted-foreground">
                        Decision path
                    </p>
                    <p className="mt-0.5 text-[11px] text-muted-foreground">
                        Separate market context, candidate, confirmation, and risk readiness.
                    </p>
                </div>
                <Badge variant="outline" className="hidden sm:inline-flex">
                    Analysis only
                </Badge>
            </div>

            <div className="grid gap-1.5 sm:grid-cols-4">
                {steps.map((step, index) => (
                    <div
                        key={step.label}
                        className={`min-w-0 rounded-lg border px-2.5 py-2 ${step.tone}`}
                    >
                        <div className="flex items-center gap-2">
                            <span className="flex size-5 shrink-0 items-center justify-center rounded-full border border-current/20 text-[9px] font-black">
                                {index + 1}
                            </span>
                            <p className="min-w-0 truncate text-[10px] font-bold uppercase tracking-[0.08em]">
                                {step.label}
                            </p>
                        </div>
                        <p className="mt-1 pl-7 text-xs font-semibold">
                            {step.state}
                        </p>
                    </div>
                ))}
            </div>
        </div>
    );
};

const LazyFeatureFallback = () => (
    <div
        className="min-h-32 w-full animate-pulse rounded-xl border border-border/60 bg-card/60 p-4"
        aria-hidden="true"
    >
        <div className="flex items-center justify-between gap-3">
            <div className="h-3 w-28 rounded bg-muted/70" />
            <div className="h-7 w-16 rounded-lg bg-muted/50" />
        </div>
        <div className="mt-4 grid gap-2 sm:grid-cols-3">
            <div className="h-10 rounded-lg bg-muted/50" />
            <div className="h-10 rounded-lg bg-muted/50" />
            <div className="h-10 rounded-lg bg-muted/50" />
        </div>
    </div>
);

type CollapsibleSectionProps = {
    title: string;
    description: string;
    open: boolean;
    onOpenChange: () => void;
    children: ReactNode;
};

const CollapsibleSection = ({
    title,
    description,
    open,
    onOpenChange,
    children,
}: CollapsibleSectionProps) => (
    <section className="mt-6 overflow-hidden rounded-2xl border border-border/60 bg-card/60 backdrop-blur-xl shadow-lg shadow-black/5 transition-all">
        <button
            type="button"
            onClick={onOpenChange}
            aria-expanded={open}
            className="flex w-full items-center justify-between gap-4 px-5 py-4 text-left transition-colors hover:bg-muted/30"
        >
            <span className="min-w-0 flex items-center gap-3">
                <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10 text-primary">
                    <BarChart3 className="h-4 w-4" />
                </div>
                <div>
                    <span className="block text-sm font-bold text-foreground">
                        {title}
                    </span>
                    <span className="mt-0.5 block text-xs text-muted-foreground">
                        {description}
                    </span>
                </div>
            </span>
            <div className="flex items-center gap-2">
                <span className="text-xs font-medium text-muted-foreground hidden sm:inline">
                    {open ? "Tutup" : "Buka"}
                </span>
                <span
                    aria-hidden="true"
                    className={`flex h-6 w-6 items-center justify-center rounded-md border border-border/60 bg-background/50 text-xs text-muted-foreground transition-transform duration-200 ${open ? "rotate-180" : ""}`}
                >
                    ▼
                </span>
            </div>
        </button>
        {open && (
            <div className="border-t border-border/60 bg-background/20">
                {children}
            </div>
        )}
    </section>
);

export default function IndexTrading({
    symbol,
    interval,
    market,
    symbols,
    intervals,
    wsUrl,
    wsDriver = "binance",
    provider = "binance",
}: IndexTradingProps) {
    const page = usePage<SharedProps>();
    const { t } = useI18n();
    const { updateActiveMarket } = useActiveMarket();
    const tickerContext = `${market}|${provider}`;
    const chartContext = `${symbol}|${market}|${interval}|${provider}`;
    const providerDisplayName = getProviderDisplayName(provider);
    const [indicators, setIndicators] = useState<IndicatorState>(() => ({
        ...DEFAULT_INDICATORS,
    }));

    const activeChartOverlayCount = CHART_OVERLAY_KEYS.filter(
        (key) => indicators[key],
    ).length;

    const clearChartOverlays = useCallback(() => {
        setIndicators((current) => {
            const next: IndicatorState = { ...current };
            CHART_OVERLAY_KEYS.forEach((key) => {
                next[key] = false;
            });
            return next;
        });
    }, []);
    const [preferencesHydrated, setPreferencesHydrated] = useState(false);
    const [tickerSnapshot, setTickerSnapshot] = useState<TickerSnapshot>(
        () => ({
            context: tickerContext,
            values: {},
        }),
    );
    const tickers =
        tickerSnapshot.context === tickerContext ? tickerSnapshot.values : {};
    const [liveQuoteSnapshot, setLiveQuoteSnapshot] =
        useState<LiveQuoteSnapshot>(() => ({
            context: chartContext,
            price: null,
            candleTime: null,
        }));
    const liveQuote =
        liveQuoteSnapshot.context === chartContext ? liveQuoteSnapshot : null;
    const livePrice = liveQuote?.price ?? null;
    const liveCandleTime = liveQuote?.candleTime ?? null;
    const [entryChartEvent, setEntryChartEvent] = useState<{
        event: SignalAlertEvent;
        time: number | null;
        price: number | null;
    } | null>(null);
    const [patternEvents, setPatternEvents] = useState<PatternEvent[]>([]);
    const [patternError, setPatternError] = useState<string | null>(null);
    const [patternStatus, setPatternStatus] = useState<
        "idle" | "loading" | "ready" | "error"
    >("idle");
    const [chartDataSnapshot, setChartDataSnapshot] =
        useState<ChartDataSnapshot>(() => ({
            context: "",
            candleTimes: [],
        }));
    const chartDataCurrent = chartDataSnapshot.context === chartContext;
    const chartCandleTimes = chartDataCurrent
        ? chartDataSnapshot.candleTimes
        : [];
    const chartDataReady = chartDataCurrent && chartCandleTimes.length > 0;
    const [volumeProfileSnapshot, setVolumeProfileSnapshot] =
        useState<VolumeProfileSnapshot>(() => ({
            context: chartContext,
            profile: null,
        }));
    const volumeProfile =
        volumeProfileSnapshot.context === chartContext
            ? volumeProfileSnapshot.profile
            : null;
    const [isPending, startTransition] = useTransition();
    const [backtestEnabled, setBacktestEnabled] = useState(false);
    const [backtestResult, setBacktestResult] = useState<BacktestResult | null>(
        null,
    );
    const [backtestChart, setBacktestChart] =
        useState<BacktestChartHandle | null>(null);
    const [selectedTradeIndex, setSelectedTradeIndex] = useState<number | null>(
        null,
    );
    const chartSeries = chartDataReady ? (backtestChart?.series ?? null) : null;
    const [signalEnabled, setSignalEnabled] = useState(false);
    const [signal, setSignal] = useState<SignalSetup | null>(null);
    const [signalMode, setSignalMode] = useState<SignalMode>(() => {
        const query = page.url.split("?")[1]?.split("#", 1)[0] ?? "";
        const requestedMode = new URLSearchParams(query)
            .get("signal_mode")
            ?.trim()
            .toLowerCase();

        return requestedMode === "balanced" || requestedMode === "flexible"
            ? requestedMode
            : "balanced";
    });
    type SignalStatus = "idle" | "loading" | "success" | "empty" | "error";
    const [signalLoading, setSignalLoading] = useState(false);
    const [signalStatus, setSignalStatus] = useState<SignalStatus>("idle");
    const [signalUpdatedAt, setSignalUpdatedAt] = useState<number | null>(null);
    const signalRequestIdRef = useRef(0);
    const signalStatusContextRef = useRef("");
    const signalErrorAlertKeyRef = useRef<string | null>(null);
    const [signalGeneration, setSignalGeneration] = useState(0);
    const signalContextRef = useRef("");
    const [signalSnapshot, setSignalSnapshot] = useState<SignalSnapshot | null>(
        null,
    );
    signalContextRef.current = `${symbol}|${market}|${interval}|${provider}|${signalMode}|${signalGeneration}`;
    const [advancedAnalyticsOpen, setAdvancedAnalyticsOpen] = useState(false);
    const [marketIntelligenceOpen, setMarketIntelligenceOpen] = useState(false);
    const [contextValidationOpen, setContextValidationOpen] = useState(false);
    const [researchOpen, setResearchOpen] = useState(false);
    type TradingViewMode = "standard" | "advanced";
    const [viewMode, setViewMode] = useState<TradingViewMode>("standard");
    const ANALYSIS_WIDGET_DEFAULTS = {
        signal_workspace: true,
        volume_profile: true,
        market_context: true,
        advanced_analytics: true,
        research: true,
    };
    const ANALYSIS_WIDGET_ORDER = [
        "signal_workspace",
        "market_context",
        "volume_profile",
        "advanced_analytics",
        "research",
    ];
    const ANALYSIS_PRESETS: WorkspacePreset[] = [
        {
            id: "overview",
            label: "Overview",
            description: "Signals first, then market context.",
            order: ["signal_workspace", "market_context", "volume_profile", "advanced_analytics", "research"],
        },
        {
            id: "research",
            label: "Research",
            description: "Context and historical analysis first.",
            order: ["market_context", "advanced_analytics", "research", "signal_workspace", "volume_profile"],
        },
        {
            id: "signals",
            label: "Signals",
            description: "Keep signal evidence close to the chart.",
            order: ["signal_workspace", "volume_profile", "market_context", "research", "advanced_analytics"],
        },
    ];
    const [analysisWidgets, setAnalysisWidgets] = useState(
        ANALYSIS_WIDGET_DEFAULTS,
    );
    const [analysisWidgetOrder, setAnalysisWidgetOrder] =
        useState(ANALYSIS_WIDGET_ORDER);
    const [activeAnalysisPreset, setActiveAnalysisPreset] =
        useState("overview");

    useEffect(() => {
        try {
            const stored = window.localStorage.getItem(
                "analysis:market-workspace:v3",
            );
            if (stored) {
                const parsed = JSON.parse(stored) as {
                    widgets?: Record<string, unknown>;
                    order?: unknown;
                    preset?: unknown;
                };

                if (parsed.widgets && typeof parsed.widgets === "object") {
                    setAnalysisWidgets({
                        ...ANALYSIS_WIDGET_DEFAULTS,
                        ...Object.fromEntries(
                            Object.keys(ANALYSIS_WIDGET_DEFAULTS).map((key) => [
                                key,
                                parsed.widgets?.[key] !== false,
                            ]),
                        ),
                    });
                }

                if (Array.isArray(parsed.order)) {
                    const known = new Set(ANALYSIS_WIDGET_ORDER);
                    const next = parsed.order.filter(
                        (id): id is string =>
                            typeof id === "string" && known.has(id),
                    );
                    ANALYSIS_WIDGET_ORDER.forEach((id) => {
                        if (!next.includes(id)) next.push(id);
                    });
                    setAnalysisWidgetOrder(next);
                }

                if (
                    typeof parsed.preset === "string" &&
                    ANALYSIS_PRESETS.some((preset) => preset.id === parsed.preset)
                ) {
                    setActiveAnalysisPreset(parsed.preset);
                }
            }
        } catch {
            // Keep defaults when localStorage is unavailable.
        }
    }, []);

    const persistAnalysisWorkspace = (
        widgets = analysisWidgets,
        order = analysisWidgetOrder,
        preset = activeAnalysisPreset,
    ) => {
        try {
            window.localStorage.setItem(
                "analysis:market-workspace:v3",
                JSON.stringify({ widgets, order, preset }),
            );
        } catch {}
    };

    const updateAnalysisWidget = (id: string, visible: boolean) => {
        setAnalysisWidgets((current) => {
            const next = { ...current, [id]: visible };
            persistAnalysisWorkspace(next);
            return next;
        });
    };

    const updateAnalysisWidgetOrder = (order: string[]) => {
        setAnalysisWidgetOrder(order);
        setActiveAnalysisPreset("");
        persistAnalysisWorkspace(analysisWidgets, order, "");
    };

    const applyAnalysisPreset = (preset: WorkspacePreset) => {
        setAnalysisWidgetOrder(preset.order);
        setActiveAnalysisPreset(preset.id);
        persistAnalysisWorkspace(analysisWidgets, preset.order, preset.id);
    };

    const resetAnalysisWidgets = () => {
        setAnalysisWidgets(ANALYSIS_WIDGET_DEFAULTS);
        setAnalysisWidgetOrder(ANALYSIS_WIDGET_ORDER);
        setActiveAnalysisPreset("overview");
        try {
            window.localStorage.removeItem("analysis:market-workspace:v3");
            persistAnalysisWorkspace(
                ANALYSIS_WIDGET_DEFAULTS,
                ANALYSIS_WIDGET_ORDER,
                "overview",
            );
        } catch {}
    };

    const analysisWidgetVisible = useCallback(
        (id: keyof typeof ANALYSIS_WIDGET_DEFAULTS) => analysisWidgets[id] !== false,
        [analysisWidgets],
    );
    const analysisWidgetPosition = useCallback(
        (id: string) => {
            const position = analysisWidgetOrder.indexOf(id);
            return position < 0 ? ANALYSIS_WIDGET_ORDER.length : position;
        },
        [analysisWidgetOrder],
    );
    const [selectedHorizon, setSelectedHorizon] = useState<TradingHorizon>(() =>
        getTradingHorizon(interval),
    );
    const activeHorizon = getTradingHorizon(interval);
    const activeHorizonOption = getTradingHorizonOption(activeHorizon);
    useEffect(() => {
        setSelectedHorizon(activeHorizon);
    }, [activeHorizon]);

    const [onboardingOpen, setOnboardingOpen] = useState(false);
    const [alerts, setAlerts] = useState<TradingAlert[]>([]);
    const previousSignalRef = useRef<SignalSetup | null>(null);
    const previousPriceRef = useRef<number | null>(null);
    const entryWarningRef = useRef(false);
    const entryReachedRef = useRef(false);
    const triggeredSignalEventsRef = useRef<Set<string>>(new Set());
    const terminalSignalEventRef = useRef<"target_hit" | "invalidated" | null>(
        null,
    );
    const activeSignalEventKeyRef = useRef<string | null>(null);

    const resetSignalEventLifecycle = useCallback(() => {
        triggeredSignalEventsRef.current.clear();
        terminalSignalEventRef.current = null;
        activeSignalEventKeyRef.current = null;
        previousPriceRef.current = null;
        entryWarningRef.current = false;
        entryReachedRef.current = false;
        setEntryChartEvent(null);
    }, []);

    const markSignalEventOnce = useCallback(
        (eventType: SignalAlertEvent) => {
            const identity =
                activeSignalEventKeyRef.current ??
                `${symbol}|${market}|${interval}|${signalMode}|${signalGeneration}`;
            const key = `${identity}:${eventType}`;
            if (triggeredSignalEventsRef.current.has(key)) return false;
            triggeredSignalEventsRef.current.add(key);
            return true;
        },
        [interval, market, signalGeneration, signalMode, symbol],
    );

    const pushAlert = useCallback(
        (alert: Omit<TradingAlert, "id" | "timestamp">) => {
            const timestamp = Date.now();
            const id = `${alert.type}-${alert.symbol}-${alert.interval}-${timestamp}-${Math.random().toString(36).slice(2, 8)}`;
            setAlerts((current) =>
                [
                    {
                        ...alert,
                        market: alert.market ?? market,
                        provider: alert.provider ?? provider,
                        id,
                        timestamp,
                        chartTime: alert.chartTime ?? liveCandleTime ?? null,
                        chartPrice: alert.chartPrice ?? livePrice ?? null,
                    },
                    ...current,
                ].slice(0, 50),
            );
        },
        [liveCandleTime, livePrice, market, provider],
    );

    const dismissAlert = useCallback((id: string) => {
        setAlerts((current) => current.filter((alert) => alert.id !== id));
    }, []);

    const clearAlerts = useCallback(() => setAlerts([]), []);

    const focusAlert = useCallback(
        (alert: TradingAlert) => {
            const targetMarket = alert.market ?? market;
            const targetProvider = alert.provider ?? provider;
            const event =
                alert.type === "direction_changed" ||
                alert.type === "refreshed" ||
                alert.type === "calculation_failed"
                    ? "detected"
                    : alert.type;
            const time =
                alert.chartTime != null && Number.isFinite(alert.chartTime)
                    ? alert.chartTime
                    : Math.floor(alert.timestamp / 1000);
            const price = alert.chartPrice;
            const query: Record<string, string> = {
                interval: alert.interval,
                focus_event: event,
                focus_time: String(time),
            };

            if (targetMarket === "futures") query.market = targetMarket;
            if (targetProvider && targetProvider !== "binance") {
                query.provider = targetProvider;
            }
            if (price != null && Number.isFinite(price) && price > 0) {
                query.focus_price = String(price);
            }

            const currentPath = new URL(page.url, window.location.origin)
                .pathname;
            const targetRoute = `/trading/${encodeURIComponent(alert.symbol)}`;
            const targetPath = currentPath.replace(
                /\/trading\/[^/]+\/?$/,
                targetRoute,
            );

            router.get(
                targetPath === currentPath ? targetRoute : targetPath,
                query,
                {
                    only: [
                        "symbol",
                        "interval",
                        "market",
                        "wsUrl",
                        "wsDriver",
                        "provider",
                    ],
                    preserveScroll: true,
                    preserveState: true,
                    replace: true,
                },
            );
        },
        [market, page.url, provider],
    );

    const signalLevel = useCallback(
        (candidate: SignalSetup | null, keys: string[]) => {
            if (!candidate) return null;
            const source = candidate as unknown as Record<string, unknown>;
            for (const key of keys) {
                const value = Number(source[key]);
                if (Number.isFinite(value) && value > 0) return value;
            }
            return null;
        },
        [],
    );

    const signalModeDescription = useMemo(() => {
        switch (signalMode) {
            case "strict":
                return "High-selectivity screening · less setup, stricter filters.";
            case "balanced":
                return "Balanced screening · balance between quality and quantity of setups.";
            case "flexible":
                return "Flexible screening · more candidates for review.";
        }
    }, [signalMode]);

    const signalDataIntegrity =
        signalStatus === "success" &&
        !signalLoading &&
        isSignalSnapshotCurrent(signalSnapshot, signal, {
            symbol,
            market,
            interval,
            provider,
            mode: signalMode,
        });

    const signalDisplayState = useMemo(
        () =>
            getSignalDisplayState(signalDataIntegrity ? signal : null, market),
        [signal, signalDataIntegrity, market],
    );

    const signalSummary = useMemo(() => {
        if (signalLoading || signalStatus === "loading") {
            return {
                label: t("Calculating"),
                detail: t(
                    "Analyzing :mode signal conditions…",
                    "Analyzing :mode signal conditions…",
                    { mode: signalMode },
                ),
            };
        }
        if (signalStatus === "error") {
            return {
                label: t("Refresh failed"),
                detail: t(
                    "The latest calculation could not be completed. The previous signal is marked stale until a new valid snapshot arrives.",
                ),
            };
        }
        if (signalStatus === "empty") {
            return {
                label: t("No setup"),
                detail: t(
                    "No qualifying :mode setup is available for the current market conditions.",
                    "No qualifying :mode setup is available for the current market conditions.",
                    { mode: signalMode },
                ),
            };
        }
        if (signalStatus === "success" && signal && !signalDataIntegrity) {
            return {
                label: t("Synchronizing signal"),
                detail: t(
                    "The signal response is available, but its snapshot context is not yet validated. The analysis scenario remains inactive until the context matches.",
                ),
            };
        }

        switch (signalDisplayState) {
            case "buy":
            case "sell":
            case "long":
            case "short":
                return {
                    label: getSignalDisplayLabel(signalDisplayState),
                    detail: t("Confidence :percent%", "Confidence :percent%", {
                        percent: Math.round(signal?.confidence ?? 0),
                    }),
                };
            case "buy_candidate":
            case "sell_candidate":
            case "long_candidate":
            case "short_candidate":
                return {
                    label: getSignalDisplayLabel(signalDisplayState),
                    detail: t(
                        "Candidate only; execution requirements are not fully met. Confidence :percent%.",
                        "Candidate only; execution requirements are not fully met. Confidence :percent%.",
                        { percent: Math.round(signal?.confidence ?? 0) },
                    ),
                };
            default:
                return {
                    label: t("No confirmed setup"),
                    detail: t(
                        "Signal data is available, but no executable directional setup is active.",
                    ),
                };
        }
    }, [
        market,
        signal,
        signalDisplayState,
        signalLoading,
        signalMode,
        signalStatus,
        signalDataIntegrity,
    ]);

    const handleSignalModeChange = useCallback(
        (mode: SignalMode) => {
            if (signalMode === mode) return;

            setSignalMode(mode);
            setSignalGeneration((generation) => generation + 1);
            setSignal(null);
            setSignalSnapshot(null);
            previousSignalRef.current = null;
            setSignalStatus("loading");
            signalStatusContextRef.current = "";
            setSignalUpdatedAt(null);
            setSignalLoading(true);
            resetSignalEventLifecycle();
        },
        [resetSignalEventLifecycle, signalMode],
    );

    const eventFocus = useMemo(() => {
        if (typeof window === "undefined") {
            return {
                event: null as SignalAlertEvent | null,
                time: null as number | null,
                price: null as number | null,
            };
        }

        const params = new URL(page.url, window.location.origin).searchParams;
        const rawEvent = params.get("focus_event");
        const event: SignalAlertEvent | null =
            rawEvent === "detected" ||
            rawEvent === "entry_reached" ||
            rawEvent === "target_hit" ||
            rawEvent === "invalidated"
                ? rawEvent
                : null;
        const rawTime = Number(params.get("focus_time"));
        const rawPrice = Number(params.get("focus_price"));

        return {
            event,
            time: Number.isFinite(rawTime) && rawTime > 0 ? rawTime : null,
            price: Number.isFinite(rawPrice) && rawPrice > 0 ? rawPrice : null,
        };
    }, [page.url]);

    useEffect(() => {
        const previousPrice = previousPriceRef.current;
        previousPriceRef.current = livePrice;
        if (livePrice == null || !signal) return;

        // Spot candidates are informational only. They must not create
        // entry/target/invalidation events before the MTF gate confirms them.
        if (!isExecutableSignal(signal)) return;

        const entry = signalLevel(signal, [
            "entry",
            "entry_price",
            "entryPrice",
        ]);
        const target = signalLevel(signal, [
            "take_profit",
            "take_profit_price",
            "takeProfit",
            "tp",
        ]);
        const invalidation = signalLevel(signal, [
            "stop_loss",
            "stop_loss_price",
            "stopLoss",
            "sl",
        ]);

        const entryZone = signal.entry_zone;
        const inEntryZone =
            entryZone != null &&
            livePrice >= Math.min(entryZone.low, entryZone.high) &&
            livePrice <= Math.max(entryZone.low, entryZone.high);
        const crossedUp = (level: number) =>
            previousPrice != null &&
            previousPrice < level &&
            livePrice >= level;
        const crossedDown = (level: number) =>
            previousPrice != null &&
            previousPrice > level &&
            livePrice <= level;

        const direction =
            signal.direction === "long" || signal.direction === "short"
                ? signal.direction
                : null;
        const targetCrossed =
            target != null &&
            (direction === "short" ? crossedDown(target) : crossedUp(target));
        const invalidationCrossed =
            invalidation != null &&
            (direction === "short"
                ? crossedUp(invalidation)
                : crossedDown(invalidation));

        if (entry != null && (crossedUp(entry) || crossedDown(entry))) {
            if (
                !entryReachedRef.current &&
                markSignalEventOnce("entry_reached")
            ) {
                if (liveCandleTime != null) {
                    setEntryChartEvent({
                        event: "entry_reached",
                        time: liveCandleTime,
                        price: livePrice,
                    });
                }
                entryReachedRef.current = true;
                entryWarningRef.current = false;
                pushAlert({
                    type: "entry_reached",
                    severity: "info",
                    title: "Entry level reached",
                    message: t(
                        "Price crossed the current signal entry level (:price).",
                        "Price crossed the current signal entry level (:price).",
                        { price: formatPrice(entry) },
                    ),
                    symbol,
                    interval,
                    mode: signalMode,
                    direction: signal.direction,
                    snapshotId: signalSnapshot?.id ?? null,
                });
            }
        } else if (
            entryZone != null &&
            liveCandleTime != null &&
            !entryReachedRef.current
        ) {
            if (
                inEntryZone &&
                !entryWarningRef.current &&
                markSignalEventOnce("entry_warning")
            ) {
                entryWarningRef.current = true;
                setEntryChartEvent({
                    event: "entry_warning",
                    time: liveCandleTime,
                    price: livePrice,
                });
            }
        }

        if (
            targetCrossed &&
            !terminalSignalEventRef.current &&
            markSignalEventOnce("target_hit")
        ) {
            terminalSignalEventRef.current = "target_hit";
            setEntryChartEvent(
                liveCandleTime != null
                    ? {
                          event: "target_hit",
                          time: liveCandleTime,
                          price: livePrice,
                      }
                    : null,
            );
            pushAlert({
                type: "target_hit",
                severity: "success",
                title: "Target level reached",
                message: t(
                    "Price crossed the current target level (:price).",
                    "Price crossed the current target level (:price).",
                    { price: formatPrice(target!) },
                ),
                symbol,
                interval,
                mode: signalMode,
                direction: signal.direction,
                snapshotId: signalSnapshot?.id ?? null,
            });
        }

        if (
            invalidationCrossed &&
            !terminalSignalEventRef.current &&
            markSignalEventOnce("invalidated")
        ) {
            terminalSignalEventRef.current = "invalidated";
            setEntryChartEvent(
                liveCandleTime != null
                    ? {
                          event: "invalidated",
                          time: liveCandleTime,
                          price: livePrice,
                      }
                    : null,
            );
            pushAlert({
                type: "invalidated",
                severity: "warning",
                title: "Invalidation level reached",
                message: t(
                    "Price crossed the current invalidation level (:price).",
                    "Price crossed the current invalidation level (:price).",
                    { price: formatPrice(invalidation!) },
                ),
                symbol,
                interval,
                mode: signalMode,
                direction: signal.direction,
                snapshotId: signalSnapshot?.id ?? null,
            });
        }
    }, [
        livePrice,
        liveCandleTime,
        signal,
        signalLevel,
        pushAlert,
        symbol,
        interval,
        signalMode,
        signalSnapshot?.id,
    ]);

    const {
        features,
        tradingCards,
        subscription: initialSubscription,
    } = page.props;
    const [subscription, setSubscription] = useState<
        TradingSubscription | null | undefined
    >(initialSubscription);
    const initialSubscriptionRef = useRef(initialSubscription);
    initialSubscriptionRef.current = initialSubscription;

    useEffect(() => {
        setSubscription(initialSubscription);
    }, [initialSubscription]);

    useEffect(() => {
        let active = true;
        let requestInFlight = false;
        let requestId = 0;
        let requestController: AbortController | null = null;
        let refreshTimer: number | undefined;

        const clearRefreshTimer = () => {
            if (refreshTimer !== undefined) {
                window.clearTimeout(refreshTimer);
                refreshTimer = undefined;
            }
        };

        const scheduleRefresh = (delay = 60_000) => {
            clearRefreshTimer();
            if (active && !document.hidden) {
                refreshTimer = window.setTimeout(() => {
                    refreshTimer = undefined;
                    void refresh();
                }, delay);
            }
        };

        const refresh = async () => {
            if (!active || requestInFlight || document.hidden) return;

            requestInFlight = true;
            const currentRequestId = ++requestId;
            const controller = new AbortController();
            requestController = controller;
            const timeout = window.setTimeout(() => controller.abort(), 15_000);
            const isCurrentRequest = () =>
                active &&
                currentRequestId === requestId &&
                !controller.signal.aborted &&
                !document.hidden;

            try {
                const response = await fetch("/subscription", {
                    headers: {
                        Accept: "application/json",
                        "X-Requested-With": "XMLHttpRequest",
                    },
                    credentials: "same-origin",
                    cache: "no-store",
                    signal: controller.signal,
                });
                if (!response.ok || !isCurrentRequest()) return;

                const payload: unknown = await response
                    .json()
                    .catch(() => null);
                if (!isCurrentRequest() || !isSubscriptionPayload(payload))
                    return;

                const trialExpired = Boolean(
                    payload.is_trial &&
                    payload.ends_at &&
                    Date.parse(payload.ends_at) <= Date.now(),
                );
                setSubscription({
                    slug: payload.plan,
                    name: payload.name,
                    features: payload.features,
                    active: payload.active,
                    ends_at: payload.ends_at,
                    trial_expired: trialExpired,
                });
            } catch {
                // Preserve the last known entitlement on transient failures.
            } finally {
                window.clearTimeout(timeout);
                if (currentRequestId === requestId) {
                    requestController = null;
                    requestInFlight = false;
                    scheduleRefresh();
                }
            }
        };

        const handleVisibilityChange = () => {
            clearRefreshTimer();
            if (document.hidden) {
                requestId += 1;
                requestController?.abort();
                requestController = null;
                requestInFlight = false;
                return;
            }
            void refresh();
        };

        document.addEventListener("visibilitychange", handleVisibilityChange);
        if (initialSubscriptionRef.current == null) {
            void refresh();
        } else {
            scheduleRefresh();
        }

        return () => {
            active = false;
            requestId += 1;
            clearRefreshTimer();
            requestController?.abort();
            document.removeEventListener(
                "visibilitychange",
                handleVisibilityChange,
            );
        };
    }, []);

    const cardVisible = useCallback(
        (card: string) => tradingCards?.[card] !== false,
        [tradingCards],
    );
    const hasFeature = useCallback(
        (feature: string) => hasSubscriptionFeature(subscription, feature),
        [subscription],
    );
    const canChart = hasFeature("chart");
    const canSignals = hasFeature("signals");
    const canBacktest = hasFeature("backtest");
    const canAdvancedInsight = hasFeature("advanced_insight");
    const canAdvancedAlerts = hasFeature("advanced_alerts");
    const backtestAvailable =
        features?.backtest_enabled !== false && canBacktest;
    const signalAvailable =
        features?.signal_engine_enabled !== false && canSignals;
    // Signal Engine is the data source for Trade Plan and chart signal
    // overlays. Keep the engine operational when either of those cards is
    // visible, even when the Signal Engine card itself is hidden by admin.
    // This makes card visibility independent without breaking dependent cards.
    const signalWorkspaceAvailable =
        signalAvailable &&
        (cardVisible("signal_engine") || cardVisible("trade_plan"));
    const signalContextCardsVisible =
        cardVisible("regime_detection") ||
        cardVisible("signal_calibration") ||
        cardVisible("signal_history") ||
        cardVisible("signal_performance");
    const contextValidationVisible =
        (signalAvailable &&
            signalEnabled &&
            (cardVisible("regime_detection") ||
                cardVisible("signal_calibration"))) ||
        cardVisible("correlation") ||
        cardVisible("portfolio_risk");
    const signalToolsAvailable =
        signalAvailable &&
        (signalWorkspaceAvailable || signalContextCardsVisible);
    const patternDetectionEnabled =
        canChart &&
        (indicators.breakout || indicators.reversal || indicators.pullback);

    useEffect(() => {
        if (signalStatus !== "error") return;

        const context = `${symbol}|${market}|${interval}|${provider}|${signalMode}|${signalGeneration}`;
        if (
            !signalEnabled ||
            !signalWorkspaceAvailable ||
            signalStatusContextRef.current !== context
        ) {
            return;
        }

        const alertKey = `${context}|${signalRequestIdRef.current}`;
        if (signalErrorAlertKeyRef.current === alertKey) return;
        signalErrorAlertKeyRef.current = alertKey;

        pushAlert({
            type: "calculation_failed",
            severity: "error",
            title: "Signal calculation failed",
            message: t(
                "The latest :mode calculation could not be completed.",
                "The latest :mode calculation could not be completed.",
                { mode: signalMode },
            ),
            symbol,
            interval,
            mode: signalMode,
            direction: signal?.direction ?? null,
            snapshotId: signalSnapshot?.id ?? null,
        });
    }, [
        interval,
        market,
        provider,
        pushAlert,
        signal,
        signalWorkspaceAvailable,
        signalEnabled,
        signalMode,
        signalSnapshot?.id,
        signalGeneration,
        signalStatus,
        symbol,
    ]);

    useEffect(() => {
        let active = true;
        let requestInFlight = false;
        let requestId = 0;
        let requestController: AbortController | null = null;
        let refreshTimer: number | undefined;
        const query = new URLSearchParams({
            symbol,
            market,
            interval,
            provider,
        });

        const clearRefreshTimer = () => {
            if (refreshTimer !== undefined) {
                window.clearTimeout(refreshTimer);
                refreshTimer = undefined;
            }
        };

        const scheduleRefresh = () => {
            clearRefreshTimer();
            if (active && patternDetectionEnabled && !document.hidden) {
                refreshTimer = window.setTimeout(() => {
                    refreshTimer = undefined;
                    void loadPatterns();
                }, getPatternRefreshDelay(interval));
            }
        };

        const isCurrentRequest = (
            currentRequestId: number,
            controller: AbortController,
        ) =>
            active &&
            currentRequestId === requestId &&
            !controller.signal.aborted &&
            !document.hidden &&
            patternDetectionEnabled;

        const loadPatterns = async () => {
            if (
                !active ||
                !patternDetectionEnabled ||
                document.hidden ||
                requestInFlight
            ) {
                return;
            }

            requestInFlight = true;
            const currentRequestId = ++requestId;
            const controller = new AbortController();
            requestController = controller;
            let timedOut = false;
            const timeout = window.setTimeout(() => {
                timedOut = true;
                controller.abort();
            }, 20_000);

            try {
                const response = await fetch(
                    `/market/patterns?${query.toString()}`,
                    {
                        headers: {
                            Accept: "application/json",
                            "X-Requested-With": "XMLHttpRequest",
                        },
                        credentials: "same-origin",
                        cache: "no-store",
                        signal: controller.signal,
                    },
                );
                const payload: unknown = await response
                    .json()
                    .catch(() => null);
                if (!isCurrentRequest(currentRequestId, controller)) return;

                if (!response.ok) {
                    const message =
                        isRecord(payload) && typeof payload.message === "string"
                            ? payload.message
                            : `Pattern API gagal (HTTP ${response.status}).`;
                    throw new Error(message);
                }

                if (!isRecord(payload) || !Array.isArray(payload.events)) {
                    throw new Error("Format respons Pattern API tidak valid.");
                }
                if (!payload.events.every(isPatternEvent)) {
                    throw new Error("Data event pola dari server tidak valid.");
                }

                setPatternEvents(payload.events);
                setPatternError(null);
                setPatternStatus("ready");
            } catch (error) {
                const isAbortError =
                    error instanceof DOMException &&
                    error.name === "AbortError";
                const isRelevant =
                    active &&
                    currentRequestId === requestId &&
                    !document.hidden &&
                    patternDetectionEnabled;
                if (isRelevant && (!isAbortError || timedOut)) {
                    setPatternError(
                        timedOut
                            ? "Pattern API timeout setelah 20 detik."
                            : error instanceof Error
                              ? error.message
                              : "Gagal memuat analisis pola market.",
                    );
                    setPatternStatus("error");
                }
            } finally {
                window.clearTimeout(timeout);
                if (currentRequestId === requestId) {
                    requestController = null;
                    requestInFlight = false;
                    scheduleRefresh();
                }
            }
        };

        const handleVisibilityChange = () => {
            clearRefreshTimer();
            if (document.hidden) {
                requestId += 1;
                requestController?.abort();
                requestController = null;
                requestInFlight = false;
                return;
            }
            void loadPatterns();
        };

        setPatternEvents([]);
        setPatternError(null);
        if (!patternDetectionEnabled) {
            setPatternStatus("idle");
            return () => {
                active = false;
                clearRefreshTimer();
                requestId += 1;
                requestController?.abort();
            };
        }

        setPatternStatus("loading");
        document.addEventListener("visibilitychange", handleVisibilityChange);
        if (!document.hidden) void loadPatterns();

        return () => {
            active = false;
            requestId += 1;
            clearRefreshTimer();
            requestController?.abort();
            document.removeEventListener(
                "visibilitychange",
                handleVisibilityChange,
            );
        };
    }, [symbol, market, interval, provider, patternDetectionEnabled]);

    useEffect(() => {
        try {
            const completed = window.localStorage.getItem(
                "trading:onboarding:v1",
            );
            if (completed !== "completed") {
                setOnboardingOpen(true);
            }
        } catch {
            setOnboardingOpen(true);
        }

        try {
            const stored = window.localStorage.getItem("trading:view-mode");
            if (stored === "standard" || stored === "advanced") {
                setViewMode(stored);
            }
        } catch {
            // Ignore unavailable localStorage.
        }
    }, []);

    const completeOnboarding = useCallback(() => {
        setOnboardingOpen(false);
        try {
            window.localStorage.setItem("trading:onboarding:v1", "completed");
        } catch {}
    }, []);

    const reopenOnboarding = useCallback(() => {
        setOnboardingOpen(true);
    }, []);

    const handleViewModeChange = useCallback((mode: TradingViewMode) => {
        setViewMode(mode);
        try {
            window.localStorage.setItem("trading:view-mode", mode);
        } catch {}

        if (mode === "standard") {
            setAdvancedAnalyticsOpen(false);
            setResearchOpen(false);
        }
    }, []);

    useEffect(() => {
        if (!signalEnabled || !signalWorkspaceAvailable) {
            setSignalEnabled(false);
            setSignal(null);
            setSignalSnapshot(null);
            previousSignalRef.current = null;
            resetSignalEventLifecycle();
            setSignalStatus("idle");
            signalStatusContextRef.current = "";
            setSignalUpdatedAt(null);
        }
    }, [signalEnabled, signalWorkspaceAvailable, resetSignalEventLifecycle]);

    useEffect(() => {
        if (!backtestAvailable) {
            setBacktestEnabled(false);
            setBacktestResult(null);
            setSelectedTradeIndex(null);
        }
    }, [backtestAvailable]);

    useEffect(() => {
        if (!canAdvancedAlerts) {
            setAlerts([]);
        }
    }, [canAdvancedAlerts]);

    useEffect(() => {
        let cancelled = false;
        const controller = new AbortController();

        const hydrate = async () => {
            try {
                const response = await fetch("/user/preferences", {
                    headers: {
                        Accept: "application/json",
                        "X-Requested-With": "XMLHttpRequest",
                    },
                    credentials: "same-origin",
                    cache: "no-store",
                    signal: controller.signal,
                });
                if (!response.ok || cancelled) return;

                const data: unknown = await response.json();
                if (!cancelled) setIndicators(normalizeIndicators(data));
            } catch {
                if (!cancelled && !controller.signal.aborted) {
                    setIndicators({ ...DEFAULT_INDICATORS });
                }
            } finally {
                if (!cancelled) setPreferencesHydrated(true);
            }
        };

        const schedule = window.setTimeout(() => void hydrate(), 250);

        return () => {
            cancelled = true;
            controller.abort();
            window.clearTimeout(schedule);
        };
    }, []);

    useEffect(() => {
        if (!preferencesHydrated) return;

        const normalized = normalizeIndicators(indicators);
        const controller = new AbortController();
        const timer = setTimeout(() => {
            void fetch("/user/preferences", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    Accept: "application/json",
                    "X-Requested-With": "XMLHttpRequest",
                    "X-CSRF-TOKEN":
                        document
                            .querySelector('meta[name="csrf-token"]')
                            ?.getAttribute("content") || "",
                },
                credentials: "same-origin",
                signal: controller.signal,
                body: JSON.stringify({ indicators: normalized }),
            }).catch(() => {});
        }, 400);

        return () => {
            clearTimeout(timer);
            controller.abort();
        };
    }, [indicators, preferencesHydrated]);

    useEffect(() => {
        let active = true;
        let requestInFlight = false;
        let requestId = 0;
        let requestController: AbortController | null = null;
        let refreshTimer: number | undefined;
        setTickerSnapshot({ context: tickerContext, values: {} });

        const clearRefreshTimer = () => {
            if (refreshTimer !== undefined) {
                window.clearTimeout(refreshTimer);
                refreshTimer = undefined;
            }
        };

        const scheduleRefresh = (delay: number) => {
            clearRefreshTimer();
            if (active && !document.hidden) {
                refreshTimer = window.setTimeout(() => {
                    refreshTimer = undefined;
                    void load();
                }, delay);
            }
        };

        const load = async () => {
            if (!active || requestInFlight || document.hidden) return;

            requestInFlight = true;
            const currentRequestId = ++requestId;
            const controller = new AbortController();
            requestController = controller;
            const timeout = window.setTimeout(() => controller.abort(), 15_000);

            try {
                const response = await fetch(
                    `/market/tickers?market=${market}&provider=${encodeURIComponent(provider)}&symbol=${encodeURIComponent(symbol)}`,
                    {
                        headers: {
                            Accept: "application/json",
                            "X-Requested-With": "XMLHttpRequest",
                        },
                        credentials: "same-origin",
                        cache: "no-store",
                        signal: controller.signal,
                    },
                );
                if (
                    !response.ok ||
                    !active ||
                    currentRequestId !== requestId ||
                    controller.signal.aborted ||
                    document.hidden
                ) {
                    return;
                }

                const payload: unknown = await response.json();
                if (
                    !active ||
                    currentRequestId !== requestId ||
                    controller.signal.aborted ||
                    document.hidden ||
                    !Array.isArray(payload)
                ) {
                    return;
                }
                const list = payload.filter(isMarketTicker);
                setTickerSnapshot({
                    context: tickerContext,
                    values: Object.fromEntries(
                        list.map((ticker) => [ticker.symbol, ticker]),
                    ),
                });
            } catch (error: unknown) {
                if (error instanceof Error && error.name !== "AbortError") {
                    // Keep the last known ticker state on transient failures.
                }
            } finally {
                window.clearTimeout(timeout);
                if (currentRequestId === requestId) {
                    requestController = null;
                    requestInFlight = false;
                    scheduleRefresh(30_000);
                }
            }
        };

        const handleVisibilityChange = () => {
            clearRefreshTimer();
            if (document.hidden) {
                requestId += 1;
                requestController?.abort();
                requestController = null;
                requestInFlight = false;
                return;
            }
            void load();
        };

        document.addEventListener("visibilitychange", handleVisibilityChange);
        scheduleRefresh(300);

        return () => {
            active = false;
            requestId += 1;
            clearRefreshTimer();
            requestController?.abort();
            document.removeEventListener(
                "visibilitychange",
                handleVisibilityChange,
            );
        };
    }, [market, provider, symbol, tickerContext]);

    useEffect(() => {
        setLiveQuoteSnapshot({
            context: "",
            price: null,
            candleTime: null,
        });
        setSignal(null);
        setSignalSnapshot(null);
        previousSignalRef.current = null;
        setSignalStatus("loading");
        signalStatusContextRef.current = "";
        setSignalUpdatedAt(null);
        setSignalLoading(true);
        setSignalGeneration((generation) => generation + 1);
        resetSignalEventLifecycle();
        setBacktestResult(null);
        setSelectedTradeIndex(null);
        // CustomChart keeps its candle series alive while it reloads data for a
        // new context. Keep that handle; its cleanup owns clearing it on unmount.
        setChartDataSnapshot({ context: "", candleTimes: [] });
        setVolumeProfileSnapshot({ context: "", profile: null });
    }, [symbol, interval, market, provider, resetSignalEventLifecycle]);

    useEffect(() => {
        if (!backtestEnabled) {
            setBacktestResult(null);
            setSelectedTradeIndex(null);
        }
    }, [backtestEnabled]);

    const navigate = (
        nextSymbol: string,
        nextInterval: string,
        nextMarket: MarketType = market,
    ) => {
        if (
            nextSymbol === symbol &&
            nextInterval === interval &&
            nextMarket === market
        ) {
            return;
        }

        startTransition(() => {
            router.get(
                `/trading/${encodeURIComponent(nextSymbol)}`,
                {
                    interval: nextInterval,
                    ...(nextMarket === "futures" ? { market: nextMarket } : {}),
                    ...(provider !== "binance" ? { provider } : {}),
                },
                {
                    only: [
                        "symbol",
                        "interval",
                        "market",
                        "wsUrl",
                        "wsDriver",
                        "provider",
                    ],
                    preserveState: true,
                    preserveScroll: true,
                    replace: true,
                },
            );
        });
    };

    const handleTradingHorizonChange = useCallback(
        (horizon: TradingHorizon) => {
            const option = getTradingHorizonOption(horizon);
            setSelectedHorizon(horizon);

            const currentIntervalAvailable = option.intervals.some(
                (value) =>
                    value.toLowerCase() === interval.trim().toLowerCase(),
            );
            if (currentIntervalAvailable) return;

            const nextInterval = option.intervals.find((value) =>
                intervals.some(
                    (available) =>
                        available.toLowerCase() === value.toLowerCase(),
                ),
            );
            if (nextInterval && nextInterval !== interval) {
                navigate(symbol, nextInterval);
            }
        },
        [interval, intervals, navigate, symbol],
    );

    const handleLastPrice = useCallback(
        (price: number, time?: number) => {
            setLiveQuoteSnapshot((current) => ({
                context: chartContext,
                price,
                candleTime:
                    time != null && Number.isFinite(time)
                        ? time
                        : current.context === chartContext
                          ? current.candleTime
                          : null,
            }));
        },
        [chartContext],
    );

    const handleCandleTimesChange = useCallback(
        (times: number[]) => {
            setChartDataSnapshot({
                context: times.length > 0 ? chartContext : "",
                candleTimes: times,
            });
        },
        [chartContext],
    );

    const handleVolumeProfile = useCallback(
        (profile: VolumeProfileResult | null) => {
            setVolumeProfileSnapshot({ context: chartContext, profile });
        },
        [chartContext],
    );

    const symbolName = useMemo(
        () => symbols[market].find((s) => s.symbol === symbol)?.name ?? symbol,
        [symbols, market, symbol],
    );
    const current = tickers[symbol];
    const price = livePrice ?? current?.last ?? null;
    const changePct = current?.change_pct ?? null;

    useEffect(() => {
        const nextSelection = {
            symbol,
            market,
            interval,
            provider,
            name: symbolName,
            price,
            changePct,
            updatedAt: price !== null ? Date.now() : null,
        };

        updateActiveMarket((previous) => {
            const sameInstrument =
                previous?.symbol === symbol &&
                previous.market === market &&
                previous.provider === provider;
            if (
                sameInstrument &&
                previous?.interval === interval &&
                previous?.name === symbolName &&
                previous?.price === price &&
                previous?.changePct === changePct
            ) {
                return previous;
            }
            return nextSelection;
        });
    }, [
        symbol,
        market,
        interval,
        provider,
        symbolName,
        price,
        changePct,
        updateActiveMarket,
    ]);

    return (
        <div className="trading-terminal min-h-full min-w-0 w-full bg-background/95 text-foreground transition-colors">
            <Head
                title={
                    price !== null
                        ? `${formatPrice(price)} ${pair(symbol)}${market === "futures" ? ` ${t("chart.perp", "Perp")}` : ""}`
                        : `${t("chart.market", "Market")} ${pair(symbol)}${market === "futures" ? ` ${t("chart.perp", "Perp")}` : ""}`
                }
            />

            <div className="min-w-0 w-full space-y-4 p-2.5 sm:space-y-6 sm:p-5 lg:space-y-7 lg:p-7">
                {/* Header Terminal Ticker & Quick Controls */}
                <div className="analysis-command-surface relative overflow-hidden rounded-2xl border p-3.5 shadow-lg backdrop-blur-xl sm:p-6 lg:p-7">
                    <div className="absolute top-0 right-0 -mt-12 -mr-12 h-48 w-48 rounded-full bg-primary/5 blur-3xl pointer-events-none" />

                    <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
                        <div className="flex flex-wrap items-center gap-3 sm:gap-4">
                            <div className="flex items-center gap-2">
                                <div className="flex h-10 w-10 items-center justify-center rounded-xl bg-primary/10 text-primary border border-primary/20 shadow-inner">
                                    <Activity className="h-5 w-5" />
                                </div>
                                <div>
                                    <div className="flex items-center gap-2">
                                        <h1 className="text-xl sm:text-2xl font-extrabold tracking-tight text-foreground">
                                            {pair(symbol)}
                                        </h1>
                                        {market === "futures" && (
                                            <span
                                                className="inline-flex items-center rounded-md bg-warning-500/10 px-2 py-0.5 text-xs font-bold text-warning-600 dark:text-warning-400 border border-warning-500/20"
                                                title={t(
                                                    "chart.perpetual_usdtm",
                                                    "USDT-M perpetual contract",
                                                )}
                                            >
                                                {t("Perp")}
                                            </span>
                                        )}
                                    </div>
                                    <p className="text-xs text-muted-foreground font-medium">
                                        {symbolName}
                                    </p>
                                </div>
                            </div>

                            <div className="h-8 w-px bg-border/60 hidden sm:block" />

                            {price !== null && (
                                <div className="flex items-baseline gap-2.5">
                                    <span className="text-3xl font-black tracking-tight text-foreground tabular-nums sm:text-4xl">
                                        ${formatPrice(price)}
                                    </span>
                                    {changePct !== null && (
                                        <span
                                            className={`inline-flex items-center gap-1 text-xs sm:text-sm font-bold tabular-nums px-2 py-0.5 rounded-lg border ${
                                                changePct >= 0
                                                    ? "bg-success-500/10 border-success-500/20 text-success"
                                                    : "bg-destructive-500/10 border-destructive-500/20 text-destructive-600 dark:text-destructive-400"
                                            }`}
                                        >
                                            {changePct >= 0 ? (
                                                <TrendingUp className="h-3.5 w-3.5" />
                                            ) : (
                                                <TrendingDown className="h-3.5 w-3.5" />
                                            )}
                                            {changePct >= 0 ? "+" : ""}
                                            {changePct.toFixed(2)}%
                                        </span>
                                    )}
                                </div>
                            )}
                        </div>

                        <div className="flex w-full flex-wrap items-center justify-end gap-2.5 border-t border-border/40 pt-2 lg:w-auto lg:border-t-0 lg:pt-0">
                            <Suspense
                                fallback={
                                    <div className="h-10 w-28 animate-pulse rounded-xl bg-muted/40" />
                                }
                            >
                                <CoinInsightSheet
                                    symbol={symbol}
                                    name={symbolName}
                                    market={market}
                                    interval={interval}
                                    provider={provider}
                                    signalMode={signalMode}
                                    workspaceSignal={signal}
                                    workspaceSignalStatus={signalStatus}
                                    workspaceSignalDataIntegrity={
                                        signalDataIntegrity
                                    }
                                    workspaceSignalEnabled={signalEnabled}
                                    workspaceSignalSummary={signalSummary}
                                />
                            </Suspense>
                            <SubscriptionGate
                                subscription={subscription}
                                feature="advanced_alerts"
                                compact
                                lockedLabel="Advanced alerts"
                            >
                                <AlertCenter
                                    alerts={alerts}
                                    onDismiss={dismissAlert}
                                    onClear={clearAlerts}
                                    onFocus={focusAlert}
                                />
                            </SubscriptionGate>
                        </div>
                    </div>

                    {/* Compact analysis horizon */}
                    <div className="mt-5 rounded-xl border border-border/60 bg-muted/15 p-3 sm:p-3.5">
                        <div className="flex flex-col gap-2.5 lg:flex-row lg:items-center lg:justify-between">
                            <div className="flex min-w-0 items-center gap-2.5">
                                <div className="flex size-7 shrink-0 items-center justify-center rounded-lg border border-primary/15 bg-primary/10 text-primary">
                                    <BarChart3 className="size-3.5" />
                                </div>
                                <div className="min-w-0">
                                    <div className="flex items-center gap-2">
                                        <span className="text-[10px] font-bold uppercase tracking-[0.16em] text-muted-foreground">
                                            {t("Analysis Horizon")}
                                        </span>
                                        <span className="text-xs font-bold text-foreground">{activeHorizonOption.label}</span>
                                    </div>
                                    <p className="truncate text-[10px] text-muted-foreground">
                                        {t("Pilih horizon untuk memfilter timeframe yang paling relevan. Signal Engine tetap mengikuti sensitivity yang dipilih.")}
                                    </p>
                                </div>
                            </div>
                            <div className="grid grid-cols-2 gap-1.5 sm:grid-cols-4 lg:w-auto lg:min-w-[34rem]">
                                {TRADING_HORIZON_OPTIONS.map((option) => {
                                    const supported = option.intervals.some((value) =>
                                        intervals.some((available) => available.toLowerCase() === value.toLowerCase()),
                                    );
                                    const active = selectedHorizon === option.id;
                                    return (
                                        <button
                                            key={option.id}
                                            type="button"
                                            disabled={!supported || isPending}
                                            aria-pressed={active}
                                            onClick={() => handleTradingHorizonChange(option.id)}
                                            className={`rounded-lg border px-2.5 py-2 text-left transition-all ${active ? "border-primary/40 bg-primary/10" : "border-border/60 bg-background/35 hover:bg-muted/40"} ${!supported || isPending ? "cursor-not-allowed opacity-50" : ""}`}
                                        >
                                            <div className="flex items-center justify-between gap-2">
                                                <span className="text-[11px] font-bold text-foreground">{option.label}</span>
                                                {active && <span className="size-1.5 rounded-full bg-primary" aria-hidden="true" />}
                                            </div>
                                            <span className="mt-0.5 block text-[9px] text-muted-foreground">{option.intervals.join(" · ")}</span>
                                        </button>
                                    );
                                })}
                            </div>
                        </div>
                    </div>

                    {/* Sub-bar Navigasi Mode & Onboarding */}
                    <div className="mt-4 flex flex-col gap-3 rounded-xl border border-border/60 bg-muted/20 p-3.5 sm:flex-row sm:items-center sm:justify-between">
                        <div className="flex items-center gap-2.5 min-w-0">
                            <div className="flex h-7 w-7 items-center justify-center rounded-lg bg-background border border-border/60 text-primary shrink-0">
                                <Sparkles className="h-3.5 w-3.5" />
                            </div>
                            <div className="min-w-0">
                                <p className="text-xs font-bold text-foreground">
                                    {t("Market Analysis Workspace")}
                                </p>
                                <p className="text-[11px] text-muted-foreground truncate">
                                    {t(
                                        "Gunakan Standard untuk fokus pada chart dan sinyal, atau Advanced untuk analitik dan riset mendalam.",
                                    )}
                                </p>
                            </div>
                        </div>
                        <div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:items-center">
                            <button
                                type="button"
                                onClick={reopenOnboarding}
                                aria-label={t("Panduan Cepat")}
                                title={t("Panduan Cepat")}
                                className="flex items-center justify-center gap-1.5 rounded-xl border border-border/70 bg-background/80 px-2.5 py-2 text-xs font-semibold text-muted-foreground transition-all hover:bg-muted/60 hover:text-foreground shadow-xs sm:px-3.5"
                            >
                                <HelpCircle className="h-3.5 w-3.5 shrink-0" />
                                <span className="hidden md:inline">
                                    {t("Panduan Cepat")}
                                </span>
                            </button>
                            <AnalysisWidgetSettings
                                settings={[
                                    { id: "signal_workspace", label: "Signal analysis", description: "Primary signal context and analysis summary." },
                                    { id: "volume_profile", label: "Volume profile", description: "Price-volume distribution and key levels." },
                                    { id: "market_context", label: "Market context", description: "Breadth, relay and regime context." },
                                    { id: "advanced_analytics", label: "Advanced analytics", description: "Historical signal performance and diagnostics." },
                                    { id: "research", label: "Research workspace", description: "Replay and historical analysis tools." },
                                ]}
                                value={analysisWidgets}
                                order={analysisWidgetOrder}
                                presets={ANALYSIS_PRESETS}
                                activePresetId={activeAnalysisPreset}
                                onChange={updateAnalysisWidget}
                                onOrderChange={updateAnalysisWidgetOrder}
                                onPresetChange={applyAnalysisPreset}
                                onReset={resetAnalysisWidgets}
                                label="Workspace"
                                className="w-full sm:w-auto"
                            />
                            <div
                                className="grid w-full grid-cols-2 rounded-xl border border-border/70 bg-background/50 p-1 sm:w-auto shadow-inner"
                                role="group"
                                aria-label={t("Trading view mode")}
                            >
                                {(
                                    [
                                        ["standard", "Standard"],
                                        ["advanced", "Advanced"],
                                    ] as const
                                ).map(([mode, label]) => (
                                    <button
                                        key={mode}
                                        type="button"
                                        aria-pressed={viewMode === mode}
                                        onClick={() =>
                                            handleViewModeChange(mode)
                                        }
                                        className={`rounded-lg px-3.5 py-1.5 text-xs font-bold transition-all ${
                                            viewMode === mode
                                                ? "bg-primary text-primary-foreground shadow-sm shadow-primary/30"
                                                : "text-muted-foreground hover:text-foreground hover:bg-muted/40"
                                        }`}
                                    >
                                        {label}
                                    </button>
                                ))}
                            </div>
                        </div>
                    </div>
                </div>

                <div id="market-analysis-snapshot" className="scroll-mt-24">
                    <MarketAnalysisSnapshot
                    symbol={symbol}
                    symbolName={symbolName}
                    market={market}
                    interval={interval}
                    provider={providerDisplayName}
                    price={price}
                    changePct={changePct}
                    horizon={activeHorizonOption.label}
                    signalLabel={signalSummary.label}
                    signalDetail={signalSummary.detail}
                    signalStatus={signalStatus}
                    signalDirection={signalDataIntegrity && signal ? signal.direction : null}
                    signalConfidence={signalDataIntegrity && signal ? signal.confidence : null}
                    />
                </div>

                <DecisionClarityRail
                    signalStatus={signalStatus}
                    signalDisplayState={signalDisplayState}
                    signalDataIntegrity={signalDataIntegrity}
                    signal={signal}
                    price={price}
                />

                <nav
                    aria-label={t("Trading analysis flow", "Trading analysis flow")}
                    className="sticky top-2 z-20 mt-2 overflow-x-auto rounded-xl border border-border/60 bg-background/90 p-1.5 shadow-sm backdrop-blur-xl sm:static sm:mt-3 sm:bg-muted/10 sm:shadow-none"
                >
                    <div className="flex min-w-max items-center gap-1 sm:min-w-0 sm:justify-center">
                        {[
                            ["#market-analysis-snapshot", "Market", "Read the current market context"],
                            ["#market-chart", "Chart", "Inspect price action and structure"],
                            ["#signal-analysis", "Signal", "Validate directional evidence"],
                            ["#trade-plan", "Plan", "Review levels and risk"],
                        ].map(([href, label, detail], index) => (
                            <a
                                key={href}
                                href={href}
                                title={detail}
                                className="group flex min-h-9 items-center gap-2 rounded-lg px-2.5 py-1.5 text-[10px] font-semibold text-muted-foreground transition-colors hover:bg-background hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 sm:min-h-8"
                            >
                                <span
                                    className={`flex size-5 items-center justify-center rounded-full border text-[9px] font-black ${
                                        index === 0
                                            ? "border-primary/30 bg-primary/10 text-primary"
                                            : "border-border/70 bg-background/50 text-muted-foreground group-hover:border-primary/20 group-hover:text-primary"
                                    }`}
                                >
                                    {index + 1}
                                </span>
                                <span>{label}</span>
                            </a>
                        ))}
                    </div>
                </nav>

                <SubscriptionLifecycleBanner subscription={subscription} />

                {/* Toolbar Grafik */}
                <TradingChartToolbar
                    indicators={indicators}
                    onIndicatorChange={(key, pressed) =>
                        setIndicators((prev) => ({
                            ...prev,
                            [key]: pressed,
                        }))
                    }
                    intervals={intervals}
                    interval={interval}
                    onIntervalChange={(value) => navigate(symbol, value)}
                    backtestAvailable={backtestAvailable}
                    backtestEnabled={backtestEnabled}
                    onBacktestChange={setBacktestEnabled}
                    signalAvailable={signalToolsAvailable}
                    signalEnabled={signalEnabled}
                    onSignalChange={setSignalEnabled}
                    onResetChart={() => backtestChart?.resetView()}
                />

                {patternDetectionEnabled && patternStatus === "error" && (
                    <div
                        className="mb-3 rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive"
                        role="alert"
                        aria-live="polite"
                    >
                        {t("Analisis pola tidak tersedia:")}{" "}
                        {patternError ?? "permintaan gagal"}
                    </div>
                )}
                {patternDetectionEnabled &&
                    chartDataReady &&
                    patternStatus === "ready" &&
                    !patternEvents.some((event) => indicators[event.type]) && (
                        <div
                            className="mb-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-2 text-xs text-muted-foreground"
                            role="status"
                            aria-live="polite"
                        >
                            {t(
                                "Belum ada pola terkonfirmasi untuk pilihan toolbar pada candle yang dimuat.",
                            )}
                        </div>
                    )}

                {/* Chart Command Center */}
                <SubscriptionGate
                    subscription={subscription}
                    feature="chart"
                    lockedLabel="Trading chart"
                >
                    <ContextMenu>
                        <ContextMenuTrigger asChild>
                            <section
                                id="market-chart"
                                aria-label={t(
                                    "Market chart workspace",
                                    "Market chart workspace",
                                )}
                                className={`relative overflow-hidden rounded-2xl border border-border/70 bg-card/80 shadow-2xl shadow-black/10 backdrop-blur-xl transition-all duration-200 ${isPending ? "opacity-70" : "opacity-100"}`}
                            >
                                <div className="flex flex-col gap-2 border-b border-border/60 bg-background/35 px-3 py-2.5 sm:gap-3 sm:flex-row sm:items-center sm:justify-between sm:px-4">
                                    <div className="flex min-w-0 items-center gap-3">
                                        <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-primary/20 bg-primary/10 text-primary">
                                            <BarChart3 className="h-4 w-4" />
                                        </div>
                                        <div className="min-w-0">
                                            <div className="flex flex-wrap items-center gap-2">
                                                <span className="text-sm font-bold tracking-tight">
                                                    {pair(symbol)}
                                                </span>
                                                <span className="rounded-md border border-border/60 bg-muted/30 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
                                                    {interval}
                                                </span>
                                                <span className="rounded-md border border-border/60 bg-muted/20 px-1.5 py-0.5 text-[10px] font-semibold text-muted-foreground">
                                                    {market === "futures"
                                                        ? t(
                                                              "Perpetual",
                                                              "Perpetual",
                                                          )
                                                        : t("Spot", "Spot")}
                                                </span>
                                            </div>
                                            <p className="truncate text-[11px] text-muted-foreground">
                                                {t(
                                                    "Price action & market structure",
                                                    "Price action & market structure",
                                                )}
                                            </p>
                                        </div>
                                    </div>
                                    <div className="flex items-center gap-2">
                                        <div className="hidden items-center gap-2 rounded-lg border border-border/50 bg-muted/20 px-2.5 py-1.5 text-[10px] text-muted-foreground sm:flex">
                                            <span className="h-1.5 w-1.5 rounded-full bg-success-400 shadow-[0_0_8px_currentColor]" />
                                            {t(
                                                "Market workspace",
                                                "Market workspace",
                                            )}
                                        </div>
                                        <div className="hidden rounded-lg border border-border/50 bg-background/40 px-2.5 py-1.5 text-[10px] font-medium text-muted-foreground sm:block">
                                            {activeChartOverlayCount > 0
                                                ? `${activeChartOverlayCount} ${t("overlays", "overlays")}`
                                                : t(
                                                      "Clean chart",
                                                      "Clean chart",
                                                  )}
                                        </div>
                                    </div>
                                </div>
                                <div className="relative">
                                    <CustomChart
                                        symbol={symbol}
                                        market={market}
                                        interval={interval}
                                        wsUrl={wsUrl}
                                        wsDriver={wsDriver}
                                        provider={provider}
                                        indicators={indicators}
                                        focusTime={eventFocus.time}
                                        onLastPrice={handleLastPrice}
                                        onChartReady={setBacktestChart}
                                        onCandleTimesChange={
                                            handleCandleTimesChange
                                        }
                                        onVolumeProfile={handleVolumeProfile}
                                        volumeProfileEnabled={
                                            cardVisible("volume_profile") &&
                                            indicators.volumeProfile
                                        }
                                    />
                                    <Suspense fallback={null}>
                                        <PatternChartOverlay
                                            series={chartSeries}
                                            events={
                                                chartDataReady
                                                    ? patternEvents
                                                    : []
                                            }
                                            candleTimes={chartCandleTimes}
                                            indicators={indicators}
                                            enabled={
                                                patternDetectionEnabled &&
                                                chartDataReady
                                            }
                                        />
                                        {backtestEnabled &&
                                            backtestAvailable &&
                                            chartDataReady && (
                                                <BacktestChartOverlay
                                                    chart={backtestChart}
                                                    result={backtestResult}
                                                    selectedTradeIndex={
                                                        selectedTradeIndex
                                                    }
                                                />
                                            )}
                                        {signalEnabled &&
                                            signalWorkspaceAvailable &&
                                            chartDataReady && (
                                                <SignalChartOverlay
                                                    series={chartSeries}
                                                    signal={
                                                        signalDataIntegrity
                                                            ? signal
                                                            : null
                                                    }
                                                    signalSnapshotId={
                                                        signalDataIntegrity
                                                            ? (signalSnapshot?.id ??
                                                              null)
                                                            : null
                                                    }
                                                    enabled={
                                                        signalDataIntegrity
                                                    }
                                                />
                                            )}
                                        {chartDataReady &&
                                            eventFocus.event &&
                                            eventFocus.time != null && (
                                                <SignalEventMarkerOverlay
                                                    series={chartSeries}
                                                    event={eventFocus.event}
                                                    time={eventFocus.time}
                                                    price={eventFocus.price}
                                                />
                                            )}
                                        {chartDataReady &&
                                            entryChartEvent?.time != null && (
                                                <SignalEventMarkerOverlay
                                                    series={chartSeries}
                                                    event={
                                                        entryChartEvent.event
                                                    }
                                                    time={entryChartEvent.time}
                                                    price={
                                                        entryChartEvent.price
                                                    }
                                                />
                                            )}
                                    </Suspense>
                                    <div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 flex items-center justify-between gap-3 border-t border-border/30 bg-gradient-to-t from-black/30 to-transparent px-3 pb-2 pt-6 text-[10px] text-muted-foreground/80 sm:px-4">
                                        <span className="min-w-0 truncate sm:hidden">
                                            {t(
                                                "Scroll to zoom · drag to explore",
                                                "Scroll to zoom · drag to explore",
                                            )}
                                        </span>
                                        <span className="hidden min-w-0 sm:inline">
                                            {t(
                                                "Scroll to zoom · drag to explore · right-click for chart actions",
                                                "Scroll to zoom · drag to explore · right-click for chart actions",
                                            )}
                                        </span>
                                        <span className="hidden shrink-0 sm:inline">
                                            {providerDisplayName}
                                        </span>
                                    </div>
                                </div>
                            </section>
                        </ContextMenuTrigger>

                        <ContextMenuContent className="w-64">
                            <ContextMenuLabel>
                                {t("Chart actions ·")}
                                {activeChartOverlayCount} {t("overlay")}
                                {activeChartOverlayCount === 1 ? "" : "s"}{" "}
                                {t("active")}
                            </ContextMenuLabel>
                            <ContextMenuItem
                                onSelect={() => backtestChart?.resetView()}
                                className="gap-2.5"
                            >
                                <RotateCcw className="h-4 w-4 text-muted-foreground" />
                                {t("Reset chart view")}
                            </ContextMenuItem>
                            <ContextMenuItem
                                onSelect={clearChartOverlays}
                                disabled={activeChartOverlayCount === 0}
                                variant="destructive"
                                className="gap-2.5"
                            >
                                <Eraser className="h-4 w-4" />
                                {t("Clear all overlays")}
                            </ContextMenuItem>

                            <ContextMenuSeparator />

                            <ContextMenuSub>
                                <ContextMenuSubTrigger>
                                    {t("Indicators")}
                                </ContextMenuSubTrigger>
                                <ContextMenuSubContent className="w-56">
                                    {(
                                        [
                                            ["volume", "Volume"],
                                            ["volumeProfile", "Volume Profile"],
                                            ["sma20", "MA 20"],
                                            ["ema50", "EMA 50"],
                                            ["bb", "Bollinger Bands"],
                                            ["rsi", "RSI 14"],
                                            ["sr", "Support / Resistance"],
                                        ] as const
                                    ).map(([key, label]) => (
                                        <ContextMenuCheckboxItem
                                            key={key}
                                            checked={Boolean(indicators[key])}
                                            onCheckedChange={(checked) =>
                                                setIndicators((current) => ({
                                                    ...current,
                                                    [key]: checked === true,
                                                }))
                                            }
                                        >
                                            {label}
                                        </ContextMenuCheckboxItem>
                                    ))}
                                </ContextMenuSubContent>
                            </ContextMenuSub>

                            <ContextMenuSub>
                                <ContextMenuSubTrigger>
                                    {t("Patterns")}
                                </ContextMenuSubTrigger>
                                <ContextMenuSubContent className="w-48">
                                    {(
                                        [
                                            ["breakout", "Breakout"],
                                            ["reversal", "Reversal"],
                                            ["pullback", "Pullback"],
                                        ] as const
                                    ).map(([key, label]) => (
                                        <ContextMenuCheckboxItem
                                            key={key}
                                            checked={Boolean(indicators[key])}
                                            onCheckedChange={(checked) =>
                                                setIndicators((current) => ({
                                                    ...current,
                                                    [key]: checked === true,
                                                }))
                                            }
                                        >
                                            {label}
                                        </ContextMenuCheckboxItem>
                                    ))}
                                </ContextMenuSubContent>
                            </ContextMenuSub>

                            <ContextMenuSub>
                                <ContextMenuSubTrigger>
                                    {t("SMC Concepts")}
                                </ContextMenuSubTrigger>
                                <ContextMenuSubContent className="w-52">
                                    {(
                                        [
                                            [
                                                "accumulation",
                                                "Accumulation Zone",
                                            ],
                                            ["ob", "Order Block"],
                                            ["fvg", "Fair Value Gap"],
                                            ["structure", "Market Structure"],
                                            ["swing", "Swing Points"],
                                        ] as const
                                    ).map(([key, label]) => (
                                        <ContextMenuCheckboxItem
                                            key={key}
                                            checked={Boolean(indicators[key])}
                                            onCheckedChange={(checked) =>
                                                setIndicators((current) => ({
                                                    ...current,
                                                    [key]: checked === true,
                                                }))
                                            }
                                        >
                                            {label}
                                        </ContextMenuCheckboxItem>
                                    ))}
                                </ContextMenuSubContent>
                            </ContextMenuSub>

                            {(signalAvailable || backtestAvailable) && (
                                <>
                                    <ContextMenuSeparator />
                                    <ContextMenuLabel>
                                        {t("Analysis tools")}
                                    </ContextMenuLabel>
                                    {signalAvailable && (
                                        <ContextMenuCheckboxItem
                                            checked={signalEnabled}
                                            onCheckedChange={(checked) =>
                                                setSignalEnabled(
                                                    checked === true,
                                                )
                                            }
                                        >
                                            {t("Signal Analysis")}
                                        </ContextMenuCheckboxItem>
                                    )}
                                    {backtestAvailable && (
                                        <ContextMenuCheckboxItem
                                            checked={backtestEnabled}
                                            onCheckedChange={(checked) =>
                                                setBacktestEnabled(
                                                    checked === true,
                                                )
                                            }
                                        >
                                            {t("Backtest")}
                                        </ContextMenuCheckboxItem>
                                    )}
                                </>
                            )}
                        </ContextMenuContent>
                    </ContextMenu>
                </SubscriptionGate>

                <div className="flex min-w-0 flex-col gap-1">
                <div
                    className="min-w-0"
                    style={{ order: analysisWidgetPosition("volume_profile") }}
                >
                {viewMode === "advanced" && analysisWidgetVisible("volume_profile") && (
                    <SubscriptionGate
                        subscription={subscription}
                        feature="advanced_insight"
                        compact
                        lockedLabel="Advanced market context"
                    >
                        {cardVisible("volume_profile") &&
                            indicators.volumeProfile && (
                                <Suspense fallback={<LazyFeatureFallback />}>
                                    <VolumeProfilePanel
                                        profile={volumeProfile}
                                        loading={!volumeProfile}
                                    />
                                </Suspense>
                            )}
                    </SubscriptionGate>
                )}

                </div>
                <div
                    className="min-w-0"
                    style={{ order: analysisWidgetPosition("signal_workspace") }}
                >
                {/* Signal Engine Workspace */}
                {analysisWidgetVisible("signal_workspace") && signalWorkspaceAvailable && signalEnabled && (
                    <SubscriptionGate
                        subscription={subscription}
                        feature="signals"
                        lockedLabel="Signal Analysis"
                    >
                        <section id="signal-analysis" className="mt-7 scroll-mt-24 overflow-hidden rounded-2xl border border-primary/15 bg-card/80 shadow-md shadow-primary/5 backdrop-blur-xl">
                            <div className="px-4 py-4 sm:px-5">
                                <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
                                    <div className="min-w-0 flex items-center gap-3">
                                        <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary border border-primary/20">
                                            <Zap className="h-4 w-4" />
                                        </div>
                                        <div className="min-w-0">
                                            <div className="flex flex-wrap items-center gap-x-3 gap-y-1">
                                                <h2 className="text-base font-bold text-foreground">
                                                    {t("Signal Analysis")}
                                                </h2>
                                                <span className="inline-flex items-center gap-1.5 text-[11px] font-semibold text-muted-foreground">
                                                    <span
                                                        className={`h-1.5 w-1.5 rounded-full ${
                                                            signalLoading ||
                                                            signalStatus ===
                                                                "loading"
                                                                ? "bg-muted-foreground/40 animate-pulse"
                                                                : signalStatus ===
                                                                    "error"
                                                                  ? "bg-destructive"
                                                                  : signalDisplayState ===
                                                                          "buy" ||
                                                                      signalDisplayState ===
                                                                          "long"
                                                                    ? "bg-success-500"
                                                                    : signalDisplayState ===
                                                                            "sell" ||
                                                                        signalDisplayState ===
                                                                            "short"
                                                                      ? "bg-destructive-500"
                                                                      : signalDisplayState ===
                                                                              "buy_candidate" ||
                                                                          signalDisplayState ===
                                                                              "sell_candidate" ||
                                                                          signalDisplayState ===
                                                                              "long_candidate" ||
                                                                          signalDisplayState ===
                                                                              "short_candidate"
                                                                        ? "bg-warning-500"
                                                                        : "bg-muted-foreground/40"
                                                        }`}
                                                    />
                                                    {signalSummary.label}
                                                </span>
                                            </div>
                                            <p className="mt-0.5 text-xs text-muted-foreground">
                                                {t(
                                                    "Evidence-driven signal context for the active market.",
                                                )}
                                            </p>
                                        </div>
                                    </div>

                                    <div className="flex flex-wrap items-center gap-2">
                                        <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                                            {t("Sensitivity")}
                                        </span>
                                        <div
                                            className="grid w-full grid-cols-3 rounded-xl border border-border/70 bg-background/70 p-0.5 sm:w-auto"
                                            role="group"
                                            aria-label={t("Signal sensitivity")}
                                        >
                                            {(
                                                [
                                                    "strict",
                                                    "balanced",
                                                    "flexible",
                                                ] as const
                                            ).map((mode) => (
                                                <button
                                                    key={mode}
                                                    type="button"
                                                    aria-pressed={
                                                        signalMode === mode
                                                    }
                                                    onClick={() =>
                                                        handleSignalModeChange(
                                                            mode,
                                                        )
                                                    }
                                                    aria-busy={
                                                        signalLoading &&
                                                        signalMode === mode
                                                    }
                                                    className={`rounded-[9px] px-2.5 py-2 text-[11px] font-bold capitalize transition-colors sm:px-3.5 sm:py-1.5 ${
                                                        signalMode === mode
                                                            ? "bg-primary text-primary-foreground shadow-sm"
                                                            : "text-muted-foreground hover:bg-muted/60 hover:text-foreground"
                                                    } ${signalLoading ? "cursor-not-allowed opacity-60" : ""}`}
                                                >
                                                    {mode}
                                                </button>
                                            ))}
                                        </div>
                                    </div>
                                </div>

                                {signal && signalDataIntegrity && (
                                    <div
                                        className="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-4"
                                        aria-label={t("Signal summary")}
                                    >
                                        <div className="rounded-xl border border-border/60 bg-background/55 px-3 py-2">
                                            <p className="text-[9px] font-bold uppercase tracking-[0.14em] text-muted-foreground">
                                                {t("Direction")}
                                            </p>
                                            <p className="mt-0.5 text-sm font-black uppercase text-foreground">
                                                {signal.direction}
                                            </p>
                                        </div>
                                        <div className="rounded-xl border border-border/60 bg-background/55 px-3 py-2">
                                            <p className="text-[9px] font-bold uppercase tracking-[0.14em] text-muted-foreground">
                                                {t("Confidence")}
                                            </p>
                                            <p className="mt-0.5 text-sm font-black tabular-nums text-foreground">
                                                {signal.confidence}%
                                            </p>
                                        </div>
                                        <div className="rounded-xl border border-border/60 bg-background/55 px-3 py-2">
                                            <p className="text-[9px] font-bold uppercase tracking-[0.14em] text-muted-foreground">
                                                {t("Mode")}
                                            </p>
                                            <p className="mt-0.5 text-sm font-bold capitalize text-foreground">
                                                {signalMode}
                                            </p>
                                        </div>
                                        <div className="rounded-xl border border-border/60 bg-background/55 px-3 py-2">
                                            <p className="text-[9px] font-bold uppercase tracking-[0.14em] text-muted-foreground">
                                                {t("State")}
                                            </p>
                                            <p className="mt-0.5 truncate text-sm font-bold text-foreground">
                                                {signalSummary.label}
                                            </p>
                                        </div>
                                    </div>
                                )}

                                <div className="mt-3 flex flex-col gap-1.5 border-t border-border/50 pt-3 sm:flex-row sm:items-center sm:justify-between">
                                    <div className="min-w-0">
                                        <span className="text-[11px] font-medium text-primary">
                                            {signalModeDescription}
                                        </span>
                                        {signalLoading && (
                                            <span
                                                className="ml-2 inline-flex items-center gap-1 text-[10px] font-semibold text-muted-foreground"
                                                role="status"
                                                aria-live="polite"
                                            >
                                                <RefreshCw
                                                    className="size-3 animate-spin"
                                                    aria-hidden="true"
                                                />
                                                {t(
                                                    "Updating signal analysis…",
                                                    "Updating signal analysis…",
                                                )}
                                            </span>
                                        )}
                                        {provider.toLowerCase() !==
                                            "binance" && (
                                            <span className="ml-2 text-[11px] text-muted-foreground">
                                                {t("Data source:")}{" "}
                                                {providerDisplayName}
                                            </span>
                                        )}
                                    </div>
                                    <div className="flex shrink-0 items-center gap-2 text-[11px] text-muted-foreground">
                                        <span>{signalSummary.detail}</span>
                                        {signalUpdatedAt &&
                                            signalStatus === "success" && (
                                                <>
                                                    <span aria-hidden="true">
                                                        ·
                                                    </span>
                                                    <span>
                                                        {new Date(
                                                            signalUpdatedAt,
                                                        ).toLocaleTimeString(
                                                            [],
                                                            {
                                                                hour: "2-digit",
                                                                minute: "2-digit",
                                                                second: "2-digit",
                                                            },
                                                        )}
                                                    </span>
                                                </>
                                            )}
                                    </div>
                                </div>
                            </div>

                            <div className="px-3.5 pt-4 pb-6 sm:px-5 sm:pt-5 sm:pb-7 lg:px-6 lg:pt-6 lg:pb-8">
                                <div
                                    className={`grid min-w-0 gap-7 xl:gap-8 xl:items-start ${
                                        cardVisible("signal_engine") &&
                                        cardVisible("trade_plan")
                                            ? "xl:grid-cols-2"
                                            : "xl:grid-cols-1"
                                    }`}
                                    aria-label={t(
                                        "Signal analysis and scenario",
                                    )}
                                >
                                    <div className="sr-only">
                                        {t("Signal analysis and scenario")}
                                    </div>
                                    {signalWorkspaceAvailable &&
                                        signalEnabled && (
                                            <div
                                                className={
                                                    cardVisible("signal_engine")
                                                        ? "min-w-0 rounded-2xl border border-border/60 bg-background/35 p-3 sm:p-4"
                                                        : "hidden"
                                                }
                                                aria-hidden={
                                                    !cardVisible("signal_engine")
                                                }
                                            >
                                                <div className="mb-3 flex items-center justify-between gap-3 px-1">
                                                    <div className="min-w-0">
                                                        <div className="flex items-center gap-2">
                                                            <span className="flex h-6 w-6 items-center justify-center rounded-md bg-primary/10 text-primary text-[10px] font-black">
                                                                1
                                                            </span>
                                                            <span className="text-[10px] font-bold uppercase tracking-[0.16em] text-foreground">
                                                                {t("Setup analysis")}
                                                            </span>
                                                        </div>
                                                        <p className="mt-1 pl-8 text-[10px] leading-4 text-muted-foreground">
                                                            {t(
                                                                "Understand the signal before reviewing the analysis scenario.",
                                                            )}
                                                        </p>
                                                    </div>
                                                    <span className="hidden h-px flex-1 bg-border/50 sm:block" />
                                                </div>
                                                <Suspense
                                                    fallback={
                                                        <LazyFeatureFallback />
                                                    }
                                                >
                                                    <SignalEngine
                                                        symbol={symbol}
                                                        market={market}
                                                        interval={interval}
                                                        provider={provider}
                                                        enabled={signalEnabled}
                                                        requestGeneration={
                                                            signalGeneration
                                                        }
                                                        signalDataIntegrity={
                                                            signalDataIntegrity
                                                        }
                                                        onSignal={(
                                                            nextSignal,
                                                            meta,
                                                        ) => {
                                                            if (
                                                                meta.requestId !==
                                                                    signalRequestIdRef.current ||
                                                                `${meta.symbol}|${meta.market}|${meta.interval}|${meta.provider}|${meta.signalMode}|${meta.requestGeneration}` !==
                                                                    signalContextRef.current
                                                            ) {
                                                                return;
                                                            }

                                                            resetSignalEventLifecycle();
                                                            setSignal(
                                                                nextSignal,
                                                            );
                                                            if (nextSignal) {
                                                                const mode =
                                                                    nextSignal.signal_mode ??
                                                                    meta.signalMode;
                                                                const generatedAt =
                                                                    Date.now();
                                                                const previous =
                                                                    previousSignalRef.current;
                                                                const snapshotId = `${mode}-${meta.requestId}-${generatedAt}`;
                                                                activeSignalEventKeyRef.current =
                                                                    snapshotId;
                                                                setSignalSnapshot(
                                                                    createSignalSnapshot(
                                                                        {
                                                                            id: snapshotId,
                                                                            requestId:
                                                                                meta.requestId,
                                                                            mode,
                                                                            symbol,
                                                                            market,
                                                                            interval,
                                                                            provider:
                                                                                meta.provider,
                                                                            signal: nextSignal,
                                                                            generatedAt,
                                                                        },
                                                                    ),
                                                                );
                                                                const nextState =
                                                                    getSignalDisplayState(
                                                                        nextSignal,
                                                                        market,
                                                                    );
                                                                const previousState =
                                                                    previous
                                                                        ? getSignalDisplayState(
                                                                              previous,
                                                                              market,
                                                                          )
                                                                        : null;
                                                                const nextLabel =
                                                                    getSignalDisplayLabel(
                                                                        nextState,
                                                                    );
                                                                const isCandidate =
                                                                    nextState ===
                                                                        "buy_candidate" ||
                                                                    nextState ===
                                                                        "sell_candidate" ||
                                                                    nextState ===
                                                                        "long_candidate" ||
                                                                    nextState ===
                                                                        "short_candidate";

                                                                if (!previous) {
                                                                    pushAlert({
                                                                        type: "detected",
                                                                        severity:
                                                                            isCandidate
                                                                                ? "info"
                                                                                : "success",
                                                                        title: isCandidate
                                                                            ? "Signal candidate detected"
                                                                            : "Signal detected",
                                                                        message:
                                                                            isCandidate
                                                                                ? `${nextLabel} is waiting for execution requirements to pass.`
                                                                                : `${nextLabel} setup detected.`,
                                                                        symbol,
                                                                        interval,
                                                                        mode,
                                                                        direction:
                                                                            nextSignal.direction !==
                                                                            "neutral"
                                                                                ? nextSignal.direction
                                                                                : (nextSignal.candidate_direction ??
                                                                                  "neutral"),
                                                                        snapshotId,
                                                                    });
                                                                } else if (
                                                                    previousState !==
                                                                    nextState
                                                                ) {
                                                                    pushAlert({
                                                                        type: "direction_changed",
                                                                        severity:
                                                                            "warning",
                                                                        title: "Signal state changed",
                                                                        message:
                                                                            t(
                                                                                "Signal changed from :from to :to.",
                                                                                "Signal changed from :from to :to.",
                                                                                {
                                                                                    from: getSignalDisplayLabel(
                                                                                        previousState ??
                                                                                            "neutral",
                                                                                    ),
                                                                                    to: nextLabel,
                                                                                },
                                                                            ),
                                                                        symbol,
                                                                        interval,
                                                                        mode,
                                                                        direction:
                                                                            nextSignal.direction !==
                                                                            "neutral"
                                                                                ? nextSignal.direction
                                                                                : (nextSignal.candidate_direction ??
                                                                                  "neutral"),
                                                                        snapshotId,
                                                                    });
                                                                } else {
                                                                    pushAlert({
                                                                        type: "refreshed",
                                                                        severity:
                                                                            "info",
                                                                        title: "Signal refreshed",
                                                                        message:
                                                                            t(
                                                                                ":label recalculated.",
                                                                                ":label recalculated.",
                                                                                {
                                                                                    label: nextLabel,
                                                                                },
                                                                            ),
                                                                        symbol,
                                                                        interval,
                                                                        mode,
                                                                        direction:
                                                                            nextSignal.direction !==
                                                                            "neutral"
                                                                                ? nextSignal.direction
                                                                                : (nextSignal.candidate_direction ??
                                                                                  "neutral"),
                                                                        snapshotId,
                                                                    });
                                                                }
                                                                previousSignalRef.current =
                                                                    nextSignal;
                                                            } else {
                                                                setSignalSnapshot(
                                                                    null,
                                                                );
                                                                previousSignalRef.current =
                                                                    null;
                                                            }
                                                        }}
                                                        signalMode={signalMode}
                                                        onLoadingChange={(
                                                            loading,
                                                            meta,
                                                        ) => {
                                                            if (
                                                                meta.requestId !==
                                                                    signalRequestIdRef.current ||
                                                                `${symbol}|${market}|${interval}|${provider}|${meta.signalMode}|${meta.requestGeneration}` !==
                                                                    signalContextRef.current
                                                            ) {
                                                                return;
                                                            }
                                                            setSignalLoading(
                                                                loading,
                                                            );
                                                        }}
                                                        onRequestIdChange={(
                                                            requestId,
                                                        ) => {
                                                            signalRequestIdRef.current =
                                                                requestId;
                                                        }}
                                                        onStatusChange={(
                                                            status,
                                                            meta,
                                                        ) => {
                                                            if (
                                                                meta.requestId !==
                                                                    signalRequestIdRef.current ||
                                                                `${symbol}|${market}|${interval}|${provider}|${meta.signalMode}|${meta.requestGeneration}` !==
                                                                    signalContextRef.current
                                                            ) {
                                                                return;
                                                            }
                                                            signalStatusContextRef.current = `${symbol}|${market}|${interval}|${provider}|${meta.signalMode}|${meta.requestGeneration}`;
                                                            setSignalStatus(
                                                                status,
                                                            );
                                                            if (
                                                                status ===
                                                                    "success" ||
                                                                status ===
                                                                    "empty"
                                                            ) {
                                                                setSignalUpdatedAt(
                                                                    Date.now(),
                                                                );
                                                            }
                                                        }}
                                                    />
                                                </Suspense>
                                            </div>
                                        )}

                                    {cardVisible("trade_plan") &&
                                        signalWorkspaceAvailable &&
                                        signalEnabled && (
                                            <div id="trade-plan" className="analysis-surface min-w-0 scroll-mt-24 rounded-2xl border border-success/15 p-3.5 sm:p-5">
                                                <div className="mb-3 flex items-center justify-between gap-3 px-1">
                                                    <div className="min-w-0">
                                                        <div className="flex items-center gap-2">
                                                            <span className="flex h-6 w-6 items-center justify-center rounded-md bg-success-500/10 text-primary text-[10px] font-black">
                                                                2
                                                            </span>
                                                            <span className="text-[10px] font-bold uppercase tracking-[0.16em] text-foreground">
                                                                {t("Analysis Scenario")}
                                                            </span>
                                                        </div>
                                                        <p className="mt-1 pl-8 text-[10px] leading-4 text-muted-foreground">
                                                            {t(
                                                                "Review the validated scenario, key levels, invalidation context, and risk considerations.",
                                                            )}
                                                        </p>
                                                    </div>
                                                    <span className="hidden h-px flex-1 bg-border/50 sm:block" />
                                                </div>
                                                <Suspense
                                                    fallback={
                                                        <LazyFeatureFallback />
                                                    }
                                                >
                                                    <TradePlan
                                                        symbol={symbol}
                                                        market={market}
                                                        interval={interval}
                                                        provider={provider}
                                                        signal={signal}
                                                        enabled={signalEnabled}
                                                        loading={signalLoading}
                                                        signalStatus={
                                                            signalStatus
                                                        }
                                                        signalUpdatedAt={
                                                            signalUpdatedAt
                                                        }
                                                        signalMode={signalMode}
                                                        signalSnapshot={
                                                            signalSnapshot
                                                        }
                                                        signalDataIntegrity={
                                                            signalDataIntegrity
                                                        }
                                                    />
                                                </Suspense>
                                            </div>
                                        )}
                                </div>
                            </div>
                        </section>
                    </SubscriptionGate>
                )}

                </div>
                <div
                    className="min-w-0"
                    style={{ order: analysisWidgetPosition("market_context") }}
                >
                {/* Market Intelligence Section */}
                {viewMode === "advanced" &&
                    analysisWidgetVisible("market_context") &&
                    cardVisible("market_intelligence") &&
                    (cardVisible("market_breadth") ||
                        cardVisible("market_relay")) && (
                    <SubscriptionGate
                        subscription={subscription}
                        feature="advanced_insight"
                        compact
                        lockedLabel="Market intelligence"
                    >
                        <CollapsibleSection
                            title={t("Market Intelligence")}
                            description={t(
                                "Supporting evidence and broader market context for the active setup.",
                            )}
                            open={marketIntelligenceOpen}
                            onOpenChange={() =>
                                setMarketIntelligenceOpen((value) => !value)
                            }
                        >
                            <div className="space-y-6 p-4 sm:p-6">
                                <div className="grid min-w-0 gap-4 xl:grid-cols-2">
                                    {cardVisible("market_breadth") && (
                                        <Suspense
                                            fallback={<LazyFeatureFallback />}
                                        >
                                            <MarketBreadthPanel
                                                market={market}
                                                provider={provider}
                                            />
                                        </Suspense>
                                    )}
                                    {cardVisible("market_relay") && (
                                        <Suspense
                                            fallback={<LazyFeatureFallback />}
                                        >
                                            <MarketRelayPanel
                                                leader={symbol}
                                                market={market}
                                                interval={interval}
                                                provider={provider}
                                            />
                                        </Suspense>
                                    )}
                                </div>
                            </div>
                        </CollapsibleSection>
                    </SubscriptionGate>
                )}

                </div>
                <div
                    className="min-w-0"
                    style={{ order: analysisWidgetPosition("market_context") }}
                >
                {/* Context & Validation Section */}
                {viewMode === "advanced" &&
                    cardVisible("context_validation") &&
                    contextValidationVisible && (
                    <CollapsibleSection
                        title={t("Context & Validation", "Context & Validation")}
                        description={t(
                            "Evidence layer for validating the active setup",
                            "Evidence layer for validating the active setup",
                        )}
                        open={contextValidationOpen}
                        onOpenChange={() =>
                            setContextValidationOpen((value) => !value)
                        }
                    >
                        <div className="space-y-3 p-4 sm:p-6">
                            <div className="flex items-end justify-between gap-4 px-1">
                                <div>
                                    <p className="text-[10px] font-bold uppercase tracking-[0.16em] text-muted-foreground">
                                        {t("Evidence layer", "Evidence layer")}
                                    </p>
                                    <h2 className="mt-1 text-sm font-bold text-foreground">
                                        {t(
                                            "Market context & signal validation",
                                            "Market context & signal validation",
                                        )}
                                    </h2>
                                </div>
                                <span className="hidden text-[11px] text-muted-foreground sm:block">
                                    {t(
                                        "Use these signals to validate the active setup",
                                        "Use these signals to validate the active setup",
                                    )}
                                </span>
                            </div>

                            <div
                                className={
                                    cardVisible("regime_detection") &&
                                    cardVisible("signal_calibration") &&
                                    signalAvailable &&
                                    signalEnabled
                                        ? "grid min-w-0 gap-4 lg:grid-cols-2 lg:items-start"
                                        : "grid min-w-0 gap-4"
                                }
                                aria-label={t(
                                    "Market context and signal validation",
                                )}
                            >
                                {cardVisible("regime_detection") &&
                                    signalAvailable &&
                                    signalEnabled && (
                                        <Suspense
                                            fallback={<LazyFeatureFallback />}
                                        >
                                            <RegimeDetectionPanel
                                                symbol={symbol}
                                                market={market}
                                                interval={interval}
                                                provider={provider}
                                            />
                                        </Suspense>
                                    )}

                                {cardVisible("signal_calibration") &&
                                    signalAvailable &&
                                    signalEnabled && (
                                        <Suspense
                                            fallback={<LazyFeatureFallback />}
                                        >
                                            <SignalCalibrationPanel
                                                symbol={symbol}
                                                market={market}
                                                interval={interval}
                                                provider={provider}
                                                signal={signal}
                                                signalMode={signalMode}
                                                enabled={signalEnabled}
                                                loading={signalLoading}
                                                signalSnapshot={signalSnapshot}
                                            />
                                        </Suspense>
                                    )}
                            </div>

                            {viewMode === "advanced" && (
                                <SubscriptionGate
                                    subscription={subscription}
                                    feature="advanced_insight"
                                    compact
                                    lockedLabel="Advanced market analytics"
                                >
                                    <div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
                                        {cardVisible("correlation") && (
                                            <Suspense
                                                fallback={
                                                    <LazyFeatureFallback />
                                                }
                                            >
                                                <CorrelationPanel
                                                    market={market}
                                                    interval={interval}
                                                    provider={provider}
                                                />
                                            </Suspense>
                                        )}
                                        {cardVisible("portfolio_risk") && (
                                            <Suspense
                                                fallback={
                                                    <LazyFeatureFallback />
                                                }
                                            >
                                                <PortfolioRiskPanel
                                                    market={market}
                                                    symbol={symbol}
                                                />
                                            </Suspense>
                                        )}
                                    </div>
                                </SubscriptionGate>
                            )}
                        </div>
                    </CollapsibleSection>
                )}

                </div>
                <div
                    className="min-w-0"
                    style={{ order: analysisWidgetPosition("advanced_analytics") }}
                >
                {/* Advanced Analytics Section */}
                {viewMode === "advanced" &&
                    analysisWidgetVisible("advanced_analytics") &&
                    cardVisible("advanced_analytics") &&
                    (cardVisible("signal_history") ||
                        cardVisible("signal_performance")) && (
                    <SubscriptionGate
                        subscription={subscription}
                        feature="advanced_insight"
                        lockedLabel="Advanced analytics"
                    >
                        <CollapsibleSection
                            title={t("Analitik Lanjutan & Histori")}
                            description="Data histori sinyal historis dan metrik performa mendalam untuk analisis lanjutan."
                            open={advancedAnalyticsOpen}
                            onOpenChange={() =>
                                setAdvancedAnalyticsOpen((value) => !value)
                            }
                        >
                            <div className="space-y-6 p-4 sm:p-6">
                                {cardVisible("signal_history") &&
                                    signalAvailable &&
                                    signalEnabled && (
                                        <Suspense
                                            fallback={<LazyFeatureFallback />}
                                        >
                                            <SignalHistory
                                                symbol={symbol}
                                                market={market}
                                                interval={interval}
                                                provider={provider}
                                                enabled={signalEnabled}
                                            />
                                        </Suspense>
                                    )}
                                {cardVisible("signal_performance") &&
                                    signalAvailable &&
                                    signalEnabled && (
                                        <Suspense
                                            fallback={<LazyFeatureFallback />}
                                        >
                                            <SignalPerformance
                                                symbol={symbol}
                                                market={market}
                                                interval={interval}
                                                enabled={signalEnabled}
                                                symbols={symbols}
                                                intervals={intervals}
                                            />
                                        </Suspense>
                                    )}
                            </div>
                        </CollapsibleSection>
                    </SubscriptionGate>
                )}

                </div>
                <div
                    className="min-w-0"
                    style={{ order: analysisWidgetPosition("research") }}
                >
                {/* Research & Backtesting Section */}
                {viewMode === "advanced" &&
                    analysisWidgetVisible("research") &&
                    cardVisible("research_backtesting") &&
                    ((cardVisible("signal_engine_replay") &&
                        backtestAvailable &&
                        backtestEnabled) ||
                        cardVisible("backtest")) && (
                    <SubscriptionGate
                        subscription={subscription}
                        feature="backtest"
                        lockedLabel="Research & backtesting"
                    >
                        <CollapsibleSection
                            title={t("Riset & Backtesting Profesional")}
                            description="Uji setup historis, jalankan replay sinyal, dan evaluasi performa analisis."
                            open={researchOpen}
                            onOpenChange={() =>
                                setResearchOpen((value) => !value)
                            }
                        >
                            <div className="space-y-6 p-4 sm:p-6">
                                {cardVisible("signal_engine_replay") &&
                                    backtestAvailable &&
                                    backtestEnabled && (
                                        <Suspense
                                            fallback={<LazyFeatureFallback />}
                                        >
                                            <SignalEngineReplayPanel
                                                symbol={symbol}
                                                market={market}
                                                interval={interval}
                                            />
                                        </Suspense>
                                    )}
                                {cardVisible("backtest") &&
                                    backtestAvailable &&
                                    backtestEnabled && (
                                        <Suspense
                                            fallback={<LazyFeatureFallback />}
                                        >
                                            <BacktestPanel
                                                symbol={symbol}
                                                market={market}
                                                interval={interval}
                                                onResult={(result) => {
                                                    setBacktestResult(result);
                                                    setSelectedTradeIndex(null);
                                                }}
                                                onSelectTrade={
                                                    setSelectedTradeIndex
                                                }
                                            />
                                        </Suspense>
                                    )}
                                {cardVisible("backtest") &&
                                    !backtestEnabled && (
                                    <div className="rounded-2xl border border-dashed border-border/80 bg-muted/20 p-8 text-center text-xs text-muted-foreground">
                                        {t(
                                            "Aktifkan Backtest dari toolbar chart di atas untuk menggunakan perangkat riset dan replay otomatis.",
                                        )}
                                    </div>
                                )}
                            </div>
                        </CollapsibleSection>
                    </SubscriptionGate>
                )}

                </div>
                </div>

                <div className="-mt-4 pt-2 border-t border-border/40 text-center sm:text-left">
                    <p className="text-xs text-muted-foreground font-medium">
                        {t(
                            "chart.market_data_from",
                            "Market data from :provider",
                            { provider: providerDisplayName },
                        )}{" "}
                        {market === "futures"
                            ? t(
                                  "chart.futures_perpetual",
                                  "Futures (USDT-M perpetual)",
                              )
                            : t("chart.spot", "Spot")}
                        .
                    </p>
                </div>

                <TradingOnboarding
                    open={onboardingOpen}
                    onComplete={completeOnboarding}
                />
            </div>
        </div>
    );
}

IndexTrading.layout = (page: ReactNode) => (
    <AppLayout breadcrumbs={[{ title: "Analytics", href: "" }]}>
        {page}
    </AppLayout>
);
