import { usePage } from "@inertiajs/react";
import type { PageProps } from "@inertiajs/core";
import {
    createContext,
    useCallback,
    useContext,
    useEffect,
    useMemo,
    useRef,
    useState,
    type ReactNode,
} from "react";
import type { MarketType } from "@/lib/market";

const STORAGE_KEY = "bulec.active-market.v1";

export type ActiveMarketSelection = {
    symbol: string;
    market: MarketType;
    interval: string;
    provider: string;
    name?: string;
    price?: number | null;
    changePct?: number | null;
    updatedAt?: number | null;
};

type ActiveMarketContextValue = {
    activeMarket: ActiveMarketSelection | null;
    updateActiveMarket: (
        next:
            | ActiveMarketSelection
            | null
            | ((current: ActiveMarketSelection | null) => ActiveMarketSelection | null),
    ) => void;
};

type SharedPageProps = PageProps & {
    symbol?: unknown;
    market?: unknown;
    interval?: unknown;
    provider?: unknown;
};

const ActiveMarketContext = createContext<ActiveMarketContextValue | null>(null);

function isMarketType(value: unknown): value is MarketType {
    return value === "spot" || value === "futures";
}

function parseStoredSelection(value: string | null): ActiveMarketSelection | null {
    if (!value) return null;

    try {
        const parsed: unknown = JSON.parse(value);
        if (!parsed || typeof parsed !== "object") return null;
        const item = parsed as Record<string, unknown>;
        if (
            typeof item.symbol !== "string" ||
            !/^[A-Z0-9]{3,30}$/.test(item.symbol) ||
            !isMarketType(item.market) ||
            typeof item.interval !== "string" ||
            typeof item.provider !== "string"
        ) {
            return null;
        }

        const updatedAt =
            typeof item.updatedAt === "number" &&
            Number.isFinite(item.updatedAt) &&
            item.updatedAt > 0
                ? item.updatedAt
                : null;
        const quoteIsFresh =
            updatedAt !== null &&
            updatedAt <= Date.now() &&
            Date.now() - updatedAt <= 10 * 60 * 1000;

        return {
            symbol: item.symbol,
            market: item.market,
            interval: item.interval,
            provider: item.provider,
            name: typeof item.name === "string" ? item.name : undefined,
            price:
                quoteIsFresh &&
                typeof item.price === "number" &&
                Number.isFinite(item.price) &&
                item.price > 0
                    ? item.price
                    : null,
            changePct:
                quoteIsFresh &&
                typeof item.changePct === "number" &&
                Number.isFinite(item.changePct)
                    ? item.changePct
                    : null,
            updatedAt: quoteIsFresh ? updatedAt : null,
        };
    } catch {
        return null;
    }
}

export function ActiveMarketProvider({ children }: { children: ReactNode }) {
    const page = usePage<SharedPageProps>();
    const [activeMarket, setActiveMarket] =
        useState<ActiveMarketSelection | null>(null);
    const lastPersistedAtRef = useRef(0);

    useEffect(() => {
        if (typeof window === "undefined") return;
        const stored = parseStoredSelection(
            window.localStorage.getItem(STORAGE_KEY),
        );
        if (stored) setActiveMarket((current) => current ?? stored);
    }, []);

    useEffect(() => {
        const props = page.props;
        if (
            typeof props.symbol !== "string" ||
            !/^[A-Z0-9]{3,30}$/i.test(props.symbol) ||
            !isMarketType(props.market) ||
            typeof props.interval !== "string"
        ) {
            return;
        }

        const symbol = props.symbol.toUpperCase();
        const market = props.market;
        const interval = props.interval;
        const provider =
            typeof props.provider === "string" && props.provider.trim()
                ? props.provider
                : "binance";

        setActiveMarket((current) => {
            const sameInstrument =
                current?.symbol === symbol &&
                current.market === market &&
                current.provider === provider;

            return {
                symbol,
                market,
                interval,
                provider,
                name: sameInstrument ? current?.name : undefined,
                price: sameInstrument ? current?.price : null,
                changePct: sameInstrument ? current?.changePct : null,
                updatedAt: sameInstrument ? current?.updatedAt : null,
            };
        });
    }, [page.url, page.props.symbol, page.props.market, page.props.interval, page.props.provider]);

    useEffect(() => {
        if (typeof window === "undefined" || !activeMarket) return;

        const elapsed = Date.now() - lastPersistedAtRef.current;
        const delay = Math.max(0, 1_000 - elapsed);
        const timer = window.setTimeout(() => {
            try {
                window.localStorage.setItem(
                    STORAGE_KEY,
                    JSON.stringify(activeMarket),
                );
                lastPersistedAtRef.current = Date.now();
            } catch {
                // Storage may be unavailable in private browsing; the in-memory context remains usable.
            }
        }, delay);

        return () => window.clearTimeout(timer);
    }, [
        activeMarket?.symbol,
        activeMarket?.market,
        activeMarket?.interval,
        activeMarket?.provider,
        activeMarket?.name,
        activeMarket?.price,
        activeMarket?.changePct,
        activeMarket?.updatedAt,
    ]);

    const updateActiveMarket = useCallback<ActiveMarketContextValue["updateActiveMarket"]>(
        (next) => {
            setActiveMarket((current) => {
                const resolved = typeof next === "function" ? next(current) : next;
                if (resolved === current) return current;
                if (
                    resolved &&
                    (!/^[A-Z0-9]{3,30}$/.test(resolved.symbol) ||
                        !isMarketType(resolved.market) ||
                        !resolved.interval ||
                        !resolved.provider)
                ) {
                    return current;
                }
                return resolved;
            });
        },
        [],
    );

    const value = useMemo(
        () => ({ activeMarket, updateActiveMarket }),
        [activeMarket, updateActiveMarket],
    );

    return (
        <ActiveMarketContext.Provider value={value}>
            {children}
        </ActiveMarketContext.Provider>
    );
}

export function useActiveMarket() {
    const value = useContext(ActiveMarketContext);
    if (!value) {
        throw new Error(
            "useActiveMarket must be used inside ActiveMarketProvider.",
        );
    }
    return value;
}
