import Protocol from "@/app/providers/ProtocolProvider/protocol/protocol";
import { createContext, useEffect, useMemo, useState } from "react";
import { usePublicKey } from "../AccountProvider/usePublicKey";
import { usePrivateKeyHash } from "../AccountProvider/usePrivateKeyHash";
import { useLogger } from "@/app/hooks/useLogger";
import { useMemory } from "../MemoryProvider/useMemory";
import { useNavigate } from "react-router-dom";
import { useSystemInformation } from "../SystemProvider/useSystemInformation";

export enum ProtocolState {
    CONNECTED,
    HANDSHAKE_EXCHANGE,
    DISCONNECTED,
    RECONNECTING,
    DEVICE_VERIFICATION_REQUIRED,
    SYNCHRONIZATION
}

export type ProtocolContextType = [Protocol|null, ProtocolState, (state: ProtocolState) => void];

export const ProtocolContext = createContext<ProtocolContextType>([null, ProtocolState.DISCONNECTED, () => {}]);

interface ProtocolProviderProps {
    children: React.ReactNode;
    serverAddress : string;
}

export function ProtocolProvider(props : ProtocolProviderProps) {
    const publicKey = usePublicKey();
    const privateKey = usePrivateKeyHash();
    const protocol = useMemo(() => {
        return new Protocol(props.serverAddress)
    }, [props.serverAddress]);
    const log = useLogger('ProtocolProvider');
    const [connect, setConnect] = useState(ProtocolState.DISCONNECTED);
    const [_, setOnlineSubscribes] = useMemory<string[]>("online_subscribes", [], true);
    const systemInfo = useSystemInformation();
    const navigate = useNavigate();
    
    useEffect(() => {
        if(publicKey.trim() == ""
            || privateKey.trim() == "" || systemInfo.id == "") {
            return;
        }
        const device = {
            deviceId: systemInfo.id,
            deviceName: systemInfo.name,
            deviceOs: systemInfo.os
        }
        protocol.connect();
        const connect = () => {
            console.info("Connected to server, starting handshake exchange");
            protocol.startHandshakeExchange(publicKey, privateKey, device);
            /**
             * Сбрасываем подписки на онлайн статусы пользователей
             * так как при переподключении они слетают
             */
            setOnlineSubscribes([]);
        }
        const reconnect = () => {
            log("Connection lost, reconnecting and starting handshake exchange");
            setConnect(ProtocolState.RECONNECTING);
        }
        const handshake_start = () => {
            log("Handshake exchange started");
            setConnect(ProtocolState.HANDSHAKE_EXCHANGE);
        }
        const handshake_complete = () => {
            log("Handshake exchange complete");
            setConnect(ProtocolState.CONNECTED);
        }
        const handshake_need_device_verification = () => {
            log("Handshake exchange needs device verification");
            setConnect(ProtocolState.DEVICE_VERIFICATION_REQUIRED);
            navigate('/deviceconfirm');
        }
        protocol.on('connect', connect);
        protocol.on('reconnect', reconnect);
        protocol.on('handshake_start', handshake_start);
        protocol.on('handshake_complete', handshake_complete);
        protocol.on('handshake_need_device_verification', handshake_need_device_verification);

        return () => {
            /**
             * Отключаем все обработчики событий при размонтировании компонента, чтобы избежать 
             * утечек памяти и некорректного поведения при повторном монтировании
             */
            protocol.off('connect', connect);
            protocol.off('reconnect', reconnect);
            protocol.off('handshake_start', handshake_start);
            protocol.off('handshake_complete', handshake_complete);
            protocol.off('handshake_need_device_verification', handshake_need_device_verification);
        }
    }, [publicKey, privateKey, systemInfo.id]);

    return (
        <ProtocolContext.Provider value={[protocol, connect, setConnect]}>
            {props.children}
        </ProtocolContext.Provider>
    );

}