import { useState, useEffect, useRef, useCallback } from 'react';
import { playTimeUpSignal } from '../utils/audio';

export type TimerStatus = 'IDLE' | 'RUNNING' | 'PAUSED' | 'FINISHED';

interface UseRestTimerProps {
    defaultTime: number; // in seconds
    onFinish?: () => void;
    autoStart?: boolean;
}

export const useRestTimer = ({ defaultTime, onFinish }: UseRestTimerProps) => {
    // Initial state function to restore from localStorage if available
    const getInitialState = () => {
        try {
            const saved = localStorage.getItem('gymflow_rest_timer');
            if (saved) {
                const parsed = JSON.parse(saved);
                // Validate parsed data structure lightly
                if (parsed && typeof parsed.timeLeft === 'number') {
                    return parsed;
                }
            }
        } catch (e) {
            console.error("Failed to parse saved timer", e);
        }
        return null;
    };

    const savedState = getInitialState();

    // If we have a saved running timer, we need to recalculate time left
    let initialTimeLeft = defaultTime;
    let initialStatus: TimerStatus = 'IDLE';
    let initialDuration = defaultTime;

    if (savedState) {
        // Only restore if running OR if the context (defaultTime) matches
        // This prevents carrying over timer state between different plan steps or sessions
        // where the default time is different.
        const contextMatch = savedState.defaultTimeSnapshot === defaultTime;
        const isRunning = savedState.status === 'RUNNING';

        if (isRunning || contextMatch) {
            initialDuration = savedState.duration || defaultTime;
            initialStatus = savedState.status;
            initialTimeLeft = savedState.timeLeft;

            if (initialStatus === 'RUNNING' && savedState.endTime) {
                const now = Date.now();
                const remaining = Math.max(0, Math.ceil((savedState.endTime - now) / 1000));
                if (remaining > 0) {
                    initialTimeLeft = remaining;
                } else {
                    initialStatus = 'FINISHED'; // It finished while we were away
                    initialTimeLeft = 0;
                }
            }
        }
    }

    const [timeLeft, setTimeLeft] = useState(initialTimeLeft);
    const [status, setStatus] = useState<TimerStatus>(initialStatus);
    const [duration, setDuration] = useState(initialDuration);

    const endTimeRef = useRef<number | null>(savedState?.endTime || null);
    const rafRef = useRef<number | null>(null);
    const prevDefaultTimeRef = useRef(defaultTime);

    // Tick function - defined before effects
    const tick = useCallback(() => {
        if (!endTimeRef.current) return;
        const now = Date.now();
        const remaining = Math.max(0, Math.ceil((endTimeRef.current - now) / 1000));

        setTimeLeft(remaining);

        if (remaining <= 0) {
            setStatus('FINISHED');
            playTimeUpSignal();
            if (onFinish) onFinish();

            endTimeRef.current = null; // Clear end time

            // Auto-reset visuals after 3 seconds of "FINISHED" state?
            setTimeout(() => {
                setStatus(prev => prev === 'FINISHED' ? 'IDLE' : prev);
                setTimeLeft(prev => prev === 0 ? duration : prev);
            }, 3000);
        } else {
            rafRef.current = requestAnimationFrame(tick);
        }
    }, [duration, onFinish]);

    // Save to localStorage whenever relevant state changes
    useEffect(() => {
        const stateToSave = {
            status,
            timeLeft,
            duration,
            endTime: endTimeRef.current,
            defaultTimeSnapshot: defaultTime // Save context
        };
        localStorage.setItem('gymflow_rest_timer', JSON.stringify(stateToSave));
    }, [status, timeLeft, duration, defaultTime]);

    // Update internal duration when defaultTime changes
    useEffect(() => {
        if (prevDefaultTimeRef.current !== defaultTime) {
            prevDefaultTimeRef.current = defaultTime;
            setDuration(defaultTime);
            // Only update visible time if IDLE. If running, it will apply on next reset.
            if (status === 'IDLE') {
                setTimeLeft(defaultTime);
            }
        }
    }, [defaultTime, status]);

    // Manage RAF based on status
    useEffect(() => {
        if (status === 'RUNNING') {
            if (!rafRef.current) {
                rafRef.current = requestAnimationFrame(tick);
            }
        } else {
            if (rafRef.current) {
                cancelAnimationFrame(rafRef.current);
                rafRef.current = null;
            }
        }

        return () => {
            if (rafRef.current) {
                cancelAnimationFrame(rafRef.current);
                rafRef.current = null;
            }
        };
    }, [status, tick]);


    const start = useCallback(() => {
        if (status === 'RUNNING') return;

        // If starting from IDLE or PAUSED
        const targetSeconds = status === 'PAUSED' ? timeLeft : duration;
        endTimeRef.current = Date.now() + targetSeconds * 1000;

        setStatus('RUNNING');
        // Effect will trigger tick
    }, [status, timeLeft, duration]);

    const pause = useCallback(() => {
        if (status !== 'RUNNING') return;
        setStatus('PAUSED');
        // Effect calls cancelAnimationFrame
        endTimeRef.current = null;
    }, [status]);

    const reset = useCallback((newDuration?: number) => {
        const nextDuration = newDuration !== undefined ? newDuration : duration;
        setDuration(nextDuration);
        setTimeLeft(nextDuration);
        setStatus('IDLE');
        endTimeRef.current = null;
        // Effect calls cancelAnimationFrame (since status becomes IDLE)
    }, [duration]);

    const addTime = useCallback((seconds: number) => {
        setDuration(prev => prev + seconds);
        if (status === 'IDLE') {
            setTimeLeft(prev => prev + seconds);
        } else if (status === 'RUNNING') {
            // Add to current target
            if (endTimeRef.current) {
                endTimeRef.current += seconds * 1000;
                // Force immediate update to avoid flicker
                const now = Date.now();
                setTimeLeft(Math.max(0, Math.ceil((endTimeRef.current - now) / 1000)));
            }
        } else if (status === 'PAUSED') {
            setTimeLeft(prev => prev + seconds);
        }
    }, [status]);


    return {
        timeLeft,
        status,
        start,
        pause,
        reset,
        addTime,
        setDuration: (val: number) => {
            setDuration(val);
            if (status === 'IDLE') setTimeLeft(val);
        }
    };
};
