import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
import { WorkoutSession, WorkoutPlan, WorkoutSet } from '../types';
import { useAuth } from './AuthContext';
import { useSession } from './SessionContext';
import {
    getActiveSession,
    updateActiveSession,
    deleteActiveSession,
    addSetToActiveSession,
    deleteSetFromActiveSession,
    updateSetInActiveSession,
    saveSession
} from '../services/sessions';
import { getPlans } from '../services/plans';
import { generateId } from '../utils/uuid';
import { logWeight } from '../services/weight';
import { useNavigate } from 'react-router-dom';

interface ActiveWorkoutContextType {
    activeSession: WorkoutSession | null;
    activePlan: WorkoutPlan | null;
    isLoading: boolean;
    startSession: (plan?: WorkoutPlan, startWeight?: number) => Promise<void>;
    endSession: () => Promise<void>;
    quitSession: () => Promise<void>;
    addSet: (set: Partial<WorkoutSet>) => Promise<void>;
    removeSet: (setId: string) => Promise<void>;
    updateSet: (setId: string, updates: Partial<WorkoutSet>) => Promise<void>;
    updateSessionNote: (note: string) => Promise<void>;
}

const ActiveWorkoutContext = createContext<ActiveWorkoutContextType | undefined>(undefined);

export const ActiveWorkoutProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
    const { currentUser, updateUser } = useAuth();
    const { refreshData: refreshHistory } = useSession(); // Access session history refresh
    const navigate = useNavigate();

    const [activeSession, setActiveSession] = useState<WorkoutSession | null>(null);
    const [activePlan, setActivePlan] = useState<WorkoutPlan | null>(null);
    const [isLoading, setIsLoading] = useState<boolean>(true);

    // Restore active session on mount
    useEffect(() => {
        const restoreActive = async () => {
            if (currentUser) {
                try {
                    const session = await getActiveSession(currentUser.id);
                    if (session) {
                        setActiveSession(session);
                        if (session.planId) {
                            // Ideally fetch specific plan, but fetching all for now to find it
                            // Or session could include plan details? Backend stores planName/ID.
                            // We need the plan object for steps.
                            const plans = await getPlans(currentUser.id);
                            const plan = plans.find(p => p.id === session.planId);
                            if (plan) setActivePlan(plan);
                        }
                    }
                } catch (e) {
                    console.error("Failed to restore active session", e);
                }
            }
            setIsLoading(false);
        };
        restoreActive();
    }, [currentUser]);

    const startSession = async (plan?: WorkoutPlan, startWeight?: number) => {
        if (!currentUser || activeSession) return;

        const newSession: WorkoutSession = {
            id: generateId(),
            startTime: Date.now(),
            type: 'STANDARD',
            userBodyWeight: startWeight,
            sets: [],
            planId: plan?.id,
            planName: plan?.name
        };

        // Optimistic update
        setActivePlan(plan || null);
        setActiveSession(newSession);
        navigate('/');

        try {
            await saveSession(currentUser.id, newSession);
            if (startWeight) {
                await logWeight(startWeight);
            }
        } catch (error) {
            console.error("Failed to start session", error);
            // Revert state?
            setActiveSession(null);
            setActivePlan(null);
        }
    };

    const endSession = async () => {
        if (activeSession && currentUser) {
            const finishedSession = { ...activeSession, endTime: Date.now() };

            // Optimistic clear
            setActiveSession(null);
            setActivePlan(null);

            try {
                await updateActiveSession(currentUser.id, finishedSession);
                await refreshHistory(); // Refresh history in SessionContext
            } catch (error) {
                console.error("Failed to end session", error);
                // Restore state? This is tricky.
                setActiveSession(activeSession);
            }
        }
    };

    const quitSession = async () => {
        if (currentUser) {
            // Optimistic clear
            setActiveSession(null);
            setActivePlan(null);

            try {
                await deleteActiveSession(currentUser.id);
            } catch (error) {
                console.error("Failed to quit session", error);
            }
        }
    };

    const addSet = async (setData: Partial<WorkoutSet>) => {
        if (activeSession && currentUser) {
            try {
                // Call API first to get ID and calculated fields if any
                // The API expects: exerciseId, reps, weight, etc.
                const response = await addSetToActiveSession(currentUser.id, setData);

                // Response should contain the new set or updated session
                // Our backend returns { success: true, newSet: ..., activeExerciseId: ... }
                // or similar. I need to type the response properly or cast it.
                // Assuming response.newSet needs to be added.

                if (response && response.newSet) {
                    setActiveSession(prev => prev ? ({
                        ...prev,
                        sets: [...prev.sets, response.newSet]
                    }) : null);
                }
            } catch (error) {
                console.error("Failed to add set", error);
            }
        }
    };

    const removeSet = async (setId: string) => {
        if (activeSession && currentUser) {
            // Optimistic
            setActiveSession(prev => prev ? ({
                ...prev,
                sets: prev.sets.filter(s => s.id !== setId)
            }) : null);

            try {
                await deleteSetFromActiveSession(currentUser.id, setId);
            } catch (error) {
                console.error("Failed to delete set", error);
                // Revert?
            }
        }
    };

    const updateSet = async (setId: string, updates: Partial<WorkoutSet>) => {
        if (activeSession && currentUser) {
            // Optimistic
            setActiveSession(prev => prev ? ({
                ...prev,
                sets: prev.sets.map(s => s.id === setId ? { ...s, ...updates } : s)
            }) : null);

            try {
                await updateSetInActiveSession(currentUser.id, setId, updates);
            } catch (error) {
                console.error("Failed to update set", error);
            }
        }
    };

    const updateSessionNote = async (note: string) => {
        if (activeSession && currentUser) {
            setActiveSession(prev => prev ? ({ ...prev, note }) : null);
            try {
                await updateActiveSession(currentUser.id, { ...activeSession, note });
            } catch (error) {
                console.error("Failed to update note", error);
            }
        }
    }

    return (
        <ActiveWorkoutContext.Provider value={{
            activeSession,
            activePlan,
            isLoading,
            startSession,
            endSession,
            quitSession,
            addSet,
            removeSet,
            updateSet,
            updateSessionNote
        }}>
            {children}
        </ActiveWorkoutContext.Provider>
    );
};

export const useActiveWorkout = () => {
    const context = useContext(ActiveWorkoutContext);
    if (context === undefined) {
        throw new Error('useActiveWorkout must be used within an ActiveWorkoutProvider');
    }
    return context;
};
