// app/admin/useAdminState.ts

"use client";

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

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

    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 };
    };

    const applyQueueToAttendance = async (baseAttendance: AttendanceDataMap) => {
        const queue = await localforage.getItem<any[]>('sync-queue') || [];
        const nextAttendance = { ...baseAttendance };

        for (const action of queue) {
            if (action.type === 'SET_ATTENDANCE') {
                const { date, youthId, shiftId, hoursWorked, weightedHours, status, note } = action.payload;
                nextAttendance[getAttendanceKey(date, youthId, shiftId)] = { date, youthId, shiftId, hoursWorked, weightedHours, status, note };
            } else if (action.type === 'REMOVE_ATTENDANCE') {
                const { date, youthId, shiftId } = action.payload;
                delete nextAttendance[getAttendanceKey(date, youthId, shiftId)];
            }
        }
        return nextAttendance;
    };

    const flushOfflineQueue = useCallback(async () => {
        try {
            const queue = await localforage.getItem<any[]>('sync-queue') || [];
            if (queue.length > 0) {
                await syncOfflineQueueDb(queue);
                await localforage.setItem('sync-queue', []);
                return true;
            }
        } catch (error) {
            console.error("Server sync failed, keeping items in offline queue for later.", error);
        }
        return false;
    }, []);

    const refreshData = useCallback(async (isInitialLoad = false) => {
        if (!isInitialLoad) setIsSyncing(true);

        try {
            if (navigator.onLine) await flushOfflineQueue();

            // NYTT: Hämta schemat via filsystemet
            const schedRes = await readJsonFile('schedule.json');
            if (schedRes.success && schedRes.data) {
                setScheduleData(schedRes.data);
                await localforage.setItem('cachedScheduleData', schedRes.data);
            }

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

            const { loadedPeriods, loadedAttendance } = mapDbDataToUI(dbPeriods);
            const finalAttendance = await applyQueueToAttendance(loadedAttendance);

            setPeriods(loadedPeriods);
            setAttendance(finalAttendance);

            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) {
            setIsOffline(true);
            const cachedSched = await localforage.getItem<any[]>('cachedScheduleData');
            if (cachedSched) setScheduleData(cachedSched);

            const cachedData = await localforage.getItem<any[]>('cachedAdminData');
            if (cachedData) {
                const { loadedPeriods, loadedAttendance } = mapDbDataToUI(cachedData);
                const finalAttendance = await applyQueueToAttendance(loadedAttendance);
                setPeriods(loadedPeriods);
                setAttendance(finalAttendance);
                if (isInitialLoad && loadedPeriods.length > 0) setActivePeriodId(loadedPeriods[0].id);
            }
        } finally {
            setIsLoadingData(false);
            setIsSyncing(false);
        }
    }, [flushOfflineQueue]);

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

    useEffect(() => {
        const interval = setInterval(() => { if (navigator.onLine) refreshData(); }, 10000);
        const handleVisibilityChange = () => { if (document.visibilityState === 'visible' && navigator.onLine) refreshData(); };
        document.addEventListener('visibilitychange', handleVisibilityChange);
        window.addEventListener('focus', handleVisibilityChange);
        return () => { clearInterval(interval); document.removeEventListener('visibilitychange', handleVisibilityChange); window.removeEventListener('focus', handleVisibilityChange); };
    }, [refreshData]);

    useEffect(() => {
        const handleOnline = async () => { setIsOffline(false); const didSync = await flushOfflineQueue(); if (didSync) refreshData(); };
        window.addEventListener('online', handleOnline); window.addEventListener('offline', () => setIsOffline(true));
        return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', () => setIsOffline(true)); };
    }, [flushOfflineQueue, refreshData]);

    const addToOfflineQueue = async (action: any) => {
        const queue = await localforage.getItem<any[]>('sync-queue') || [];
        const filteredQueue = queue.filter(q => !(q.payload.date === action.payload.date && q.payload.youthId === action.payload.youthId && q.payload.shiftId === action.payload.shiftId));
        filteredQueue.push(action);
        await localforage.setItem('sync-queue', filteredQueue);
        setIsOffline(true);
    };

    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(); };

    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 || '';

        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(async () => { await 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 }[]) => {
        setAttendance(prev => { const next = { ...prev }; records.forEach(record => { next[getAttendanceKey(record.date, record.youthId, record.shiftId)] = record; }); return next; });

        const processOfflineQueue = async () => {
            const queue = await localforage.getItem<any[]>('sync-queue') || [];
            records.forEach(record => {
                const idx = queue.findIndex(q => q.payload.date === record.date && q.payload.youthId === record.youthId && q.payload.shiftId === record.shiftId);
                const action = { type: 'SET_ATTENDANCE', payload: record };
                if (idx > -1) queue[idx] = action; else queue.push(action);
            });
            await localforage.setItem('sync-queue', queue);
            setIsOffline(true);
        };

        if (!navigator.onLine) await processOfflineQueue();
        else await bulkSetAttendanceDb(records).catch(async () => { await processOfflineQueue(); });
    };

    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(async () => { await 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(async () => { await addToOfflineQueue({ type: 'REMOVE_ATTENDANCE', payload: { date, youthId, shiftId } }); });
    };

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