import React, { createContext, useContext, useEffect, useState, useCallback, type ReactNode } from 'react';
import type { Socket } from 'socket.io-client';
import { initSocket, disconnectSocket, getSocket, isSocketConnected } from '@/socket';
import { SocketStatus, type SocketContextState, type NotificationPayload } from '@/types/socket';

interface SocketProviderProps {
    children: ReactNode;
    user?: {
        id: number;
        name: string;
        email?: string;
        avatar?: string;
        socket_token?: string;
    } | null;
}

interface SocketContextValue extends SocketContextState {
    connect: () => void;
    disconnect: () => void;
    emit: <T = any>(event: string, data: T) => void;
    on: <T = any>(event: string, callback: (data: T) => void) => void;
    off: (event: string, callback?: (...args: any[]) => void) => void;
}

const SocketContext = createContext<SocketContextValue | undefined>(undefined);

export const SocketProvider: React.FC<SocketProviderProps> = ({ children, user }) => {
    const [socket, setSocket] = useState<Socket | null>(null);
    const [status, setStatus] = useState<SocketStatus>(SocketStatus.DISCONNECTED);
    const [error, setError] = useState<Error | null>(null);

    // Connect to socket
    const connect = useCallback(() => {
        if (!user) {
            console.warn('[SocketProvider] Cannot connect - no authenticated user');
            return;
        }

        if (isSocketConnected()) {
            console.log('[SocketProvider] Already connected');
            return;
        }

        try {
            setStatus(SocketStatus.CONNECTING);
            setError(null);

            const socketInstance = initSocket(
                {
                    id: user.id,
                    name: user.name,
                    email: user.email,
                    avatar: user.avatar,
                },
                user.socket_token // optional auth token from backend
            );

            // Setup connection event listeners
            socketInstance.on('connect', () => {
                console.log('[SocketProvider] Connected successfully');
                setStatus(SocketStatus.CONNECTED);
                setError(null);
            });

            socketInstance.on('disconnect', (reason) => {
                console.log('[SocketProvider] Disconnected:', reason);
                setStatus(SocketStatus.DISCONNECTED);
            });

            socketInstance.on('connect_error', (err) => {
                console.error('[SocketProvider] Connection error:', err.message);
                setStatus(SocketStatus.ERROR);
                setError(err);
            });

            socketInstance.on('reconnect', () => {
                console.log('[SocketProvider] Reconnected');
                setStatus(SocketStatus.CONNECTED);
                setError(null);
            });

            setSocket(socketInstance);
        } catch (err) {
            console.error('[SocketProvider] Failed to initialize socket:', err);
            setStatus(SocketStatus.ERROR);
            setError(err as Error);
        }
    }, [user]);

    // Disconnect from socket
    const disconnect = useCallback(() => {
        disconnectSocket();
        setSocket(null);
        setStatus(SocketStatus.DISCONNECTED);
        setError(null);
    }, []);

    // Emit event
    const emit = useCallback(
        <T = any>(event: string, data: T) => {
            if (!socket || !socket.connected) {
                console.error('[SocketProvider] Cannot emit - socket not connected');
                return;
            }
            socket.emit(event, data);
        },
        [socket]
    );

    // Add event listener
    const on = useCallback(
        <T = any>(event: string, callback: (data: T) => void) => {
            if (!socket) {
                console.error('[SocketProvider] Cannot add listener - socket not initialized');
                return;
            }
            socket.on(event, callback);
        },
        [socket]
    );

    // Remove event listener
    const off = useCallback(
        (event: string, callback?: (...args: any[]) => void) => {
            if (!socket) {
                return;
            }
            if (callback) {
                socket.off(event, callback);
            } else {
                socket.off(event);
            }
        },
        [socket]
    );

    // Auto-connect when user is authenticated
    useEffect(() => {
        if (user) {
            connect();
        }

        // Cleanup on unmount
        return () => {
            disconnect();
        };
    }, [user?.id]); // Only reconnect if user id changes

    const value: SocketContextValue = {
        socket,
        status,
        isConnected: status === SocketStatus.CONNECTED,
        error,
        connect,
        disconnect,
        emit,
        on,
        off,
    };

    return <SocketContext.Provider value={value}>{children}</SocketContext.Provider>;
};

// Custom hook to use socket context
export const useSocketContext = (): SocketContextValue => {
    const context = useContext(SocketContext);
    if (context === undefined) {
        throw new Error('useSocketContext must be used within a SocketProvider');
    }
    return context;
};

// Safe version that returns null if not in provider
export const useSocketContextSafe = (): SocketContextValue | null => {
    const context = useContext(SocketContext);
    return context || null;
};
