import { useDatabase } from "@/app/providers/DatabaseProvider/useDatabase";
import { OnlineState, PacketOnlineState, PublicKeyOnlineState } from "@/app/providers/ProtocolProvider/protocol/packets/packet.onlinestate";
import { createContext, useEffect, useState } from "react";
import { usePacket } from "../ProtocolProvider/usePacket";
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
import { useSystemAccounts } from "../SystemAccountsProvider/useSystemAccounts";
import { usePublicKey } from "../AccountProvider/usePublicKey";

export const InformationContext = createContext<any>({});

interface InformationProviderProps {
    children: React.ReactNode;
}

export interface UserInformation {
    publicKey: string;
    verified: number;
    title: string;
    username: string;
    online: OnlineState;
}

export interface GroupInformation {
    groupId: string;
    title: string;
    description: string;
}

export function InformationProvider(props: InformationProviderProps) {
    const [cachedUsers, setCachedUsers] = useState<UserInformation[]>([]);
    const [cachedGroups, setCachedGroups] = useState<GroupInformation[]>([]);
    const {allQuery, getQuery, runQuery} = useDatabase();
    const {info} = useConsoleLogger("InformationProvider");
    const systemAccounts = useSystemAccounts();
    const publicKey = usePublicKey();
    
    useEffect(() => {
        loadCachedUsers();
        loadCachedGroups();
    }, [publicKey]);

    usePacket(0x5, (state: PacketOnlineState) => {
        const keys = state.getPublicKeysState();
        keys.map((value : PublicKeyOnlineState) => {
            const cachedUser = cachedUsers.find((userInfo) => userInfo.publicKey == value.publicKey);
            if(!cachedUser) {
                info(`No cached user found for public key: ${value.publicKey}, info not updated`);
                return;
            }
            updateUserInformation({
                ...cachedUser,
                online: value.state
            });
        })
    });

    const loadCachedGroups = () => {
        if(publicKey == ''){
            return;
        }
        const result = allQuery("SELECT * FROM groups WHERE account = ?", [publicKey]);
        result.then((rows) => {
            const infos : GroupInformation[] = [];
            for(let i = 0; i < rows.length; i++) {
                infos.push({
                    groupId: rows[i].group_id,
                    title: rows[i].title,
                    description: rows[i].description,
                });
            }
            setCachedGroups(infos);
        });
    }

    const loadCachedUsers = () => {
        const result = allQuery("SELECT * FROM cached_users", []);
        result.then((rows) => {
            const infos : UserInformation[] = [];
            for(let i = 0; i < rows.length; i++) {
                infos.push({
                    publicKey: rows[i].public_key,
                    verified: rows[i].verified,
                    title: rows[i].title,
                    username: rows[i].username,
                    online: publicKey == rows[i].public_key ? OnlineState.ONLINE : OnlineState.OFFLINE
                });
            }
            infos.push(...systemAccounts);
            setCachedUsers(infos);
        });
    }

    const updateGroupInformation = async (groupInfo : GroupInformation) => {
        const result = await getQuery("SELECT COUNT(*) as count FROM groups WHERE account = ? AND group_id = ?", [publicKey, groupInfo.groupId]);
        if(result.count > 0){
            /**
             * Обрабатываем только событие если строка в базе уже есть,
             * потому что добавление строки мы отрабатываем в другом месте
             */
            await runQuery(`UPDATE groups SET title = ?, description = ? WHERE account = ? AND group_id = ?`, 
                [groupInfo.title, groupInfo.description, publicKey,  groupInfo.groupId]);
        }

        if(cachedGroups.find((v) => v.groupId == groupInfo.groupId)){
            setCachedGroups((prev) => prev.map((group) => {
                if (group.groupId == groupInfo.groupId) {
                    return {
                        ...group,
                        title: groupInfo.title,
                        description: groupInfo.description
                    }
                }
                return group;
            }));
        }else{
            setCachedGroups((prev) => ([
                ...prev,
                {
                    groupId: groupInfo.groupId,
                    title: groupInfo.title,
                    description: groupInfo.description
                }
            ]));
        }
    }

    const updateUserInformation = async (userInfo : UserInformation) => {
        const result = await getQuery("SELECT COUNT(*) as count FROM cached_users WHERE public_key = ?", [userInfo.publicKey]);
        if (result.count > 0) {
            await runQuery(`UPDATE cached_users SET title = ?, username = ?, verified = ? WHERE public_key = ?`, [userInfo.title, userInfo.username, userInfo.verified, userInfo.publicKey]);
            setCachedUsers((prev) => prev.map((user) => {
                if (user.publicKey == userInfo.publicKey) {
                    return {
                        ...user,
                        verified: userInfo.verified,
                        title: userInfo.title,
                        username: userInfo.username,
                        online: userInfo.online,
                    }
                }
                return user;
            }));
        } else {
            await runQuery(`INSERT INTO cached_users (public_key, title, username, verified) VALUES (?, ?, ?, ?)`, 
                [userInfo.publicKey, userInfo.title, userInfo.username, userInfo.verified]);
            setCachedUsers((prev) => ([
                ...prev,
                {
                    publicKey: userInfo.publicKey,
                    title: userInfo.title,
                    username: userInfo.username,
                    online: OnlineState.OFFLINE,
                    verified: userInfo.verified
                }
            ]));
        }
    }


    return (
        <InformationContext.Provider value={{
            cachedUsers,
            cachedGroups,
            setCachedUsers,
            updateUserInformation,
            updateGroupInformation
        }}>
            {props.children}
        </InformationContext.Provider>
    )
}