// app/admin/useAdminState.ts

"use client";

import { useState, useEffect, useCallback } from 'react';
import localforage from 'localforage';
import { Period, AttendanceDataMap, Youth, toIsoDate, isWeekend, getAttendanceKey } from './adminTypes';
import { getAdminData, createPeriodDb, deletePeriodDb, bulkAddYouthDb, removeYouthDb, setAttendanceDb, removeAttendanceDb, syncOfflineQueueDb, bulkSetAttendanceDb } from '../actions/admin';

export const useAdminState = () => {
    const [periods, setPeriods] = useState<Period[]>([]);
    const [attendance, setAttendance] = useState<AttendanceDataMap>({});
    const [activePeriodId, setActivePeriodId] = useState<string>('');
    const [isLoadingData, setIsLoadingData] = useState<boolean>(true);
    const [isOffline, setIsOffline] = useState<boolean>(false);

    // --- 1. DATA MAPPING ---
    const mapDbDataToUI = (dbPeriods: any[]) => {
        const loadedPeriods: Period[] = [];
        const loadedAttendance: AttendanceDataMap = {};

        for (const dbPeriod of dbPeriods) {
            const youthList: Youth[] = dbPeriod.youths.map((y: any) => ({ id: y.id, name: y.name, team: y.team as 'PF' | 'TU' }));
            loadedPeriods.push({ id: dbPeriod.id, name: dbPeriod.name, startDate: dbPeriod.startDate, endDate: dbPeriod.endDate, youthList });

            for (const youth of dbPeriod.youths) {
                for (const att of youth.attendance) {
                    const key = getAttendanceKey(att.date, att.youthId, att.shiftId);
                    loadedAttendance[key] = {
                        date: att.date, youthId: att.youthId, shiftId: att.shiftId as 'MORNING' | 'AFTERNOON',
                        hoursWorked: att.hoursWorked, weightedHours: att.weightedHours, status: att.status as any, note: att.note || ''
                    };
                }
            }
        }
        return { loadedPeriods, loadedAttendance };
    };

    // --- 2. OFFLINE QUEUE FLUSHER ---
    const flushOfflineQueue = useCallback(async () => {
        try {
            const queue = await localforage.getItem<any[]>('sync-queue') || [];
            if (queue.length > 0) {
                console.log(`Flushing ${queue.length} items from offline queue...`, queue);
                await syncOfflineQueueDb(queue);

                // ONLY clear the queue if the server action succeeds without throwing an error
                await localforage.setItem('sync-queue', []);
                return true; // Indicates we successfully synced something
            }
        } catch (error) {
            console.error("Server sync failed, keeping items in offline queue for later.", error);
        }
        return false;
    }, []);

    // --- 3. MAIN DATA FETCHING ---
    const refreshData = useCallback(async (isInitialLoad = false) => {
        try {
            // First, check if we are online and have a backlog to sync before pulling fresh data
            if (navigator.onLine) {
                await flushOfflineQueue();
            }

            const dbPeriods = await getAdminData();
            await localforage.setItem('cachedAdminData', dbPeriods);
            setIsOffline(false);

            const { loadedPeriods, loadedAttendance } = mapDbDataToUI(dbPeriods);
            setPeriods(loadedPeriods);
            setAttendance(loadedAttendance);

            // Auto-select the active period if it's the initial load
            if (isInitialLoad && loadedPeriods.length > 0) {
                const today = toIsoDate(new Date());
                const activePeriods = loadedPeriods.filter(p => today >= p.startDate && today <= p.endDate);

                if (activePeriods.length > 0) {
                    setActivePeriodId(activePeriods[0].id);
                } else {
                    setActivePeriodId(loadedPeriods[0].id);
                }
            }
        } catch (error) {
            console.error("Offline or Server Error! Loading from cache...");
            setIsOffline(true);
            const cachedData = await localforage.getItem<any[]>('cachedAdminData');

            if (cachedData) {
                const { loadedPeriods, loadedAttendance } = mapDbDataToUI(cachedData);
                setPeriods(loadedPeriods);
                setAttendance(loadedAttendance);
                if (isInitialLoad && loadedPeriods.length > 0) setActivePeriodId(loadedPeriods[0].id);
            }
        } finally {
            setIsLoadingData(false);
        }
    }, [flushOfflineQueue]);

    // Initial load
    useEffect(() => {
        refreshData(true);
    }, [refreshData]);

    // --- 4. OFFLINE/ONLINE EVENT LISTENERS ---
    useEffect(() => {
        const handleOnline = async () => {
            setIsOffline(false);
            const didSync = await flushOfflineQueue();
            if (didSync) {
                // If we successfully pushed old data, pull the fresh DB state so the UI updates
                refreshData();
            }
        };

        window.addEventListener('online', handleOnline);
        window.addEventListener('offline', () => setIsOffline(true));

        return () => {
            window.removeEventListener('online', handleOnline);
            window.removeEventListener('offline', () => setIsOffline(true));
        };
    }, [flushOfflineQueue, refreshData]);

    // Helper to add actions to local queue
    const addToOfflineQueue = async (action: any) => {
        const queue = await localforage.getItem<any[]>('sync-queue') || [];
        queue.push(action);
        await localforage.setItem('sync-queue', queue);
    };

    // --- 5. DATABASE WRITE ACTIONS ---
    const createPeriod = async (name: string, startDateStr: string) => {
        setIsLoadingData(true);
        const start = new Date(startDateStr + 'T12:00:00');
        const end = new Date(start); end.setDate(start.getDate() + 20);
        const newPeriod = await createPeriodDb(name, toIsoDate(start), toIsoDate(end));
        await refreshData();
        setActivePeriodId(newPeriod.id);
    };

    const deletePeriod = async (periodId: string) => {
        setPeriods(periods.filter(p => p.id !== periodId));
        await deletePeriodDb(periodId);
        await refreshData();
    };

    const bulkAddYouth = async (periodId: string, text: string, defaultTeam: 'PF' | 'TU') => {
        setIsLoadingData(true);
        const newYouthData = text.split('\n').map(l => l.trim()).filter(l => l.length > 0).map((line) => {
            const parts = line.split(/[,|-]/).map(p => p.trim());
            return { name: parts[0], team: (parts[1]?.toUpperCase() === 'PF' || parts[1]?.toUpperCase() === 'TU') ? parts[1].toUpperCase() : defaultTeam };
        });
        await bulkAddYouthDb(periodId, newYouthData);
        await refreshData();
    };

    const removeYouth = async (periodId: string, youthId: string) => {
        setPeriods(periods.map(p => p.id === periodId ? { ...p, youthList: p.youthList.filter(y => y.id !== youthId) } : p));
        await removeYouthDb(youthId);
        await refreshData();
    };

    // --- 6. ATTENDANCE ACTIONS (WITH OFFLINE QUEUE SUPPORT) ---
    const setManualAttendance = async (date: string, youthId: string, shiftId: 'MORNING' | 'AFTERNOON', status: 'Absent' | 'Late' | 'Present', hoursManual: number = 0, note?: string) => {
        const weight = isWeekend(date) ? 1.5 : 1.0;
        let weightedHours = status === 'Late' || status === 'Present' ? hoursManual * weight : 0;
        let hoursWorked = status === 'Late' || status === 'Present' ? hoursManual : 0;
        const noteStr = note || '';

        // Optimistic UI update
        setAttendance(prev => ({
            ...prev, [getAttendanceKey(date, youthId, shiftId)]: { date, youthId, shiftId, hoursWorked, weightedHours, status, note: noteStr }
        }));

        if (!navigator.onLine) {
            await addToOfflineQueue({ type: 'SET_ATTENDANCE', payload: { date, youthId, shiftId, hoursWorked, weightedHours, status, note: noteStr } });
        } else {
            await setAttendanceDb(date, youthId, shiftId, hoursWorked, weightedHours, status, noteStr).catch(() => {
                // If it fails (e.g., server timeout despite being "online"), put it in the queue
                addToOfflineQueue({ type: 'SET_ATTENDANCE', payload: { date, youthId, shiftId, hoursWorked, weightedHours, status, note: noteStr } });
            });
        }
    };

    const bulkSetManualAttendance = async (records: { date: string; youthId: string; shiftId: 'MORNING' | 'AFTERNOON'; hoursWorked: number; weightedHours: number; status: 'Absent' | 'Late' | 'Present'; note: string }[]) => {
        // 1. Optimistic UI update (Instant feedback for all youths)
        setAttendance(prev => {
            const next = { ...prev };
            records.forEach(record => {
                next[getAttendanceKey(record.date, record.youthId, record.shiftId)] = record;
            });
            return next;
        });

        // 2. Offline Queue Handling
        if (!navigator.onLine) {
            const queue = await localforage.getItem<any[]>('sync-queue') || [];
            // We can safely push them as individual actions to the queue to re-use the existing sync logic
            records.forEach(record => {
                queue.push({ type: 'SET_ATTENDANCE', payload: record });
            });
            await localforage.setItem('sync-queue', queue);
        } else {
            // 3. Single Network Request
            await bulkSetAttendanceDb(records).catch(async () => {
                // Fallback to queue if the server times out
                const queue = await localforage.getItem<any[]>('sync-queue') || [];
                records.forEach(record => {
                    queue.push({ type: 'SET_ATTENDANCE', payload: record });
                });
                await localforage.setItem('sync-queue', queue);
            });
        }
    };

    const addPendingAttendance = async (date: string, youthId: string, shiftId: 'MORNING' | 'AFTERNOON') => {
        setAttendance(prev => ({ ...prev, [getAttendanceKey(date, youthId, shiftId)]: { date, youthId, shiftId, hoursWorked: 0, weightedHours: 0, status: 'Pending', note: '' } }));

        if (!navigator.onLine) {
            await addToOfflineQueue({ type: 'SET_ATTENDANCE', payload: { date, youthId, shiftId, hoursWorked: 0, weightedHours: 0, status: 'Pending', note: '' } });
        } else {
            await setAttendanceDb(date, youthId, shiftId, 0, 0, 'Pending', '').catch(() => {
                addToOfflineQueue({ type: 'SET_ATTENDANCE', payload: { date, youthId, shiftId, hoursWorked: 0, weightedHours: 0, status: 'Pending', note: '' } });
            });
        }
    };

    const removeAttendanceEntry = async (date: string, youthId: string, shiftId: 'MORNING' | 'AFTERNOON') => {
        setAttendance(prev => { const next = { ...prev }; delete next[getAttendanceKey(date, youthId, shiftId)]; return next; });

        if (!navigator.onLine) {
            await addToOfflineQueue({ type: 'REMOVE_ATTENDANCE', payload: { date, youthId, shiftId } });
        } else {
            await removeAttendanceDb(date, youthId, shiftId).catch(() => {
                addToOfflineQueue({ type: 'REMOVE_ATTENDANCE', payload: { date, youthId, shiftId } });
            });
        }
    };

    return {
        periods,
        attendance,
        activePeriodId,
        setActivePeriodId,
        isLoadingData,
        isOffline,
        createPeriod,
        deletePeriod,
        bulkAddYouth,
        removeYouth,
        setManualAttendance,
        bulkSetManualAttendance,
        addPendingAttendance,
        removeAttendanceEntry
    };
};