import { useCallback, useEffect, useRef, useState } from 'react';
import { postYieldJson } from '../lib/yield-api';
import type { PriceDay, YieldCalendarResponse, YieldSeries } from '../types';

/** Number of uids requested per yield.calendar POST (keeps payloads small). */
const CHUNK_SIZE = 25;

/**
 * Un lot de la grille. `background: true` — l'hydratation part en rafale au
 * chargement et n'a pas à faire clignoter la barre de chargement globale.
 *
 * Lève sur échec (statut non-2xx comme rupture réseau) : les appelants
 * ci-dessous s'en servent pour re-tenter le lot au prochain passage.
 */
async function fetchChunk(uids: string[]): Promise<YieldSeries> {
    const result = await postYieldJson<YieldCalendarResponse>(route('yield.calendar'), { uids }, { background: true });
    if (!result.ok) throw new Error(result.failure.message);
    return result.data?.series ?? {};
}

function chunk<T>(items: T[], size: number): T[][] {
    const out: T[][] = [];
    for (let i = 0; i < items.length; i += size) {
        out.push(items.slice(i, i + size));
    }
    return out;
}

interface UseYieldSeriesResult {
    series: YieldSeries;
    /** Overwrite one uid's series (used for live slide-over preview). */
    setSeriesForUid: (uid: string, days: PriceDay[]) => void;
    /** Re-fetch a single uid from the backend (used after a save). */
    refreshUid: (uid: string) => Promise<void>;
}

/**
 * Lazily hydrates the day-grid: after mount, chunks the configured uids and
 * POSTs yield.calendar per chunk (sequentially), merging each response into
 * `series` so cells fill progressively. Exposes helpers to overwrite a single
 * uid (live preview) and to refresh a single uid (post-save reconciliation).
 *
 * `configuredUids` is re-read on change but only newly-unseen uids are fetched,
 * so re-renders from search filtering don't refetch.
 */
export function useYieldSeries(configuredUids: string[]): UseYieldSeriesResult {
    const [series, setSeries] = useState<YieldSeries>({});
    const requestedRef = useRef<Set<string>>(new Set());

    const setSeriesForUid = useCallback((uid: string, days: PriceDay[]) => {
        setSeries((prev) => ({ ...prev, [uid]: days }));
    }, []);

    const refreshUid = useCallback(async (uid: string) => {
        try {
            const merged = await fetchChunk([uid]);
            if (merged[uid]) {
                setSeries((prev) => ({ ...prev, [uid]: merged[uid] }));
            }
        } catch {
            // silent — keep the last-known series for this uid
        }
    }, []);

    useEffect(() => {
        const pending = configuredUids.filter((uid) => !requestedRef.current.has(uid));
        if (pending.length === 0) return;
        pending.forEach((uid) => requestedRef.current.add(uid));

        let cancelled = false;
        (async () => {
            for (const group of chunk(pending, CHUNK_SIZE)) {
                try {
                    const merged = await fetchChunk(group);
                    if (cancelled) return;
                    setSeries((prev) => ({ ...prev, ...merged }));
                } catch {
                    // allow retry on a later effect run by un-marking this group
                    if (!cancelled) group.forEach((uid) => requestedRef.current.delete(uid));
                }
            }
        })();

        return () => {
            cancelled = true;
        };
    }, [configuredUids]);

    return { series, setSeriesForUid, refreshUid };
}
