import { create } from 'zustand';
import { logger } from '../debugConfig';
import { isValidNodeType } from '../utils/tldraw/cc-base/cc-graph/cc-graph-types';
import {
    NavigationStore,
    NavigationNode,
    NeoGraphNode,
    MainContext,
    BaseContext,
    NavigationContextState,
    isProfileContext,
    isInstituteContext,
    addToHistory,
    navigateHistory,
    getCurrentHistoryNode,
    ExtendedContext,
    UnifiedContextSwitch,
} from '../types/navigation';

interface WhiteboardRoom {
    id: string;
    user_id: string;
    name: string;
    context_type: string;
    is_default: boolean;
    storage_path: string | null;
    neo4j_node_id: string | null;
    neo4j_db_name: string | null;
    node_type: string | null;
}

interface NavigationStoreWithAuth extends NavigationStore {
    _accessToken: string | null;
    _userId: string | null;
    setAuthInfo: (token: string | null, userId: string | null) => void;
}

const initialState: NavigationContextState = {
    main: 'profile',
    base: 'profile',
    node: null,
    history: { nodes: [], currentIndex: -1 }
};

function getDefaultBaseForMain(main: MainContext): BaseContext {
    return main === 'profile' ? 'profile' : 'school';
}

function validateContextTransition(
    current: NavigationContextState,
    updates: Partial<NavigationContextState>
): NavigationContextState {
    const newState = { ...current, ...updates };
    if (updates.main) {
        newState.base = getDefaultBaseForMain(updates.main);
    }
    if (updates.base) {
        const isValid = newState.main === 'profile'
            ? isProfileContext(updates.base)
            : isInstituteContext(updates.base);
        if (!isValid) {
            newState.base = getDefaultBaseForMain(newState.main);
        }
    }
    return newState;
}

export const useNavigationStore = create<NavigationStoreWithAuth>((set, get) => {
    const pgFetch = async <T = unknown>(
        method: 'GET' | 'POST' | 'PATCH' | 'DELETE',
        table: string,
        options: { body?: object; query?: string; prefer?: string; single?: boolean } = {}
    ): Promise<T | null> => {
        const token = get()._accessToken;
        if (!token) throw new Error('pgFetch: no access token');
        const url = `${import.meta.env.VITE_SUPABASE_URL}/rest/v1/${table}${options.query ? `?${options.query}` : ''}`;
        const headers: Record<string, string> = {
            'Authorization': `Bearer ${token}`,
            'apikey': import.meta.env.VITE_SUPABASE_ANON_KEY,
            'Content-Type': 'application/json',
        };
        if (options.prefer) headers['Prefer'] = options.prefer;
        if (options.single) headers['Accept'] = 'application/vnd.pgrst.object+json';
        const res = await fetch(url, {
            method,
            headers,
            ...(options.body ? { body: JSON.stringify(options.body) } : {}),
        });
        if (!res.ok) {
            const err = await res.text();
            throw new Error(`PostgREST ${res.status}: ${err}`);
        }
        if (res.status === 204) return null;
        return res.json() as Promise<T>;
    };

    const getOrCreateDefaultRoom = async (contextType: string): Promise<NavigationNode> => {
        const userId = get()._userId;
        if (!userId) throw new Error('getOrCreateDefaultRoom: no user ID');

        const rooms = await pgFetch<WhiteboardRoom[]>('GET', 'whiteboard_rooms', {
            query: `user_id=eq.${userId}&context_type=eq.${contextType}&is_default=eq.true`,
        });

        if (rooms && rooms.length > 0) {
            const room = rooms[0];
            return {
                id: room.id,
                node_storage_path: room.storage_path || `${userId}/workspaces/${contextType}_default.json`,
                label: room.name,
                type: 'workspace',
            };
        }

        const storagePath = `${userId}/workspaces/${contextType}_default.json`;
        const room = await pgFetch<WhiteboardRoom>('POST', 'whiteboard_rooms', {
            body: {
                user_id: userId,
                name: `${contextType.charAt(0).toUpperCase() + contextType.slice(1)} Workspace`,
                context_type: contextType,
                is_default: true,
                storage_path: storagePath,
            },
            prefer: 'return=representation',
            single: true,
        });

        if (!room) throw new Error('Failed to create default whiteboard room');
        logger.debug('navigation-context', '✅ Created default whiteboard room', { contextType, roomId: room.id });

        return {
            id: room.id,
            node_storage_path: room.storage_path || storagePath,
            label: room.name,
            type: 'workspace',
        };
    };

    return {
        _accessToken: null,
        _userId: null,
        setAuthInfo: (token: string | null, userId: string | null) => {
            set({ _accessToken: token, _userId: userId });
        },

        context: initialState,
        isLoading: false,
        error: null,

        switchContext: async (contextSwitch: UnifiedContextSwitch, _userDbName: string | null = null, _workerDbName: string | null = null) => {
            if (!get()._accessToken || !get()._userId) {
                logger.warn('navigation-context', '⚠️ switchContext called without auth — skipping');
                return;
            }
            try {
                set({ isLoading: true, error: null });
                const currentState = get().context;
                let newState: NavigationContextState = { ...currentState, node: null };

                if (contextSwitch.main) {
                    newState = validateContextTransition(newState, { main: contextSwitch.main });
                    if (!contextSwitch.skipBaseContextLoad) {
                        newState.base = getDefaultBaseForMain(contextSwitch.main);
                    }
                }
                if (contextSwitch.base) {
                    newState = validateContextTransition(newState, { base: contextSwitch.base });
                }

                const targetContext = contextSwitch.base ||
                    contextSwitch.extended ||
                    (contextSwitch.main ? getDefaultBaseForMain(contextSwitch.main) : newState.base);

                const defaultNode = await getOrCreateDefaultRoom(targetContext);
                const newHistory = addToHistory(currentState.history, defaultNode);

                set({
                    context: { ...newState, node: defaultNode, history: newHistory },
                    isLoading: false,
                    error: null,
                });
                logger.debug('navigation-context', '✅ Context switch complete', {
                    main: newState.main, base: newState.base, nodeId: defaultNode.id,
                });
            } catch (error) {
                logger.error('navigation-context', '❌ switchContext failed', error);
                set({ error: error instanceof Error ? error.message : 'Failed to switch context', isLoading: false });
            }
        },

        goBack: () => {
            const currentState = get().context;
            if (currentState.history.currentIndex > 0) {
                const newHistory = navigateHistory(currentState.history, currentState.history.currentIndex - 1);
                set({ context: { ...currentState, node: getCurrentHistoryNode(newHistory), history: newHistory } });
            }
        },

        goForward: () => {
            const currentState = get().context;
            if (currentState.history.currentIndex < currentState.history.nodes.length - 1) {
                const newHistory = navigateHistory(currentState.history, currentState.history.currentIndex + 1);
                set({ context: { ...currentState, node: getCurrentHistoryNode(newHistory), history: newHistory } });
            }
        },

        setMainContext: async (main: MainContext, userDbName: string | null, workerDbName: string | null) => {
            await get().switchContext({ main }, userDbName, workerDbName);
        },

        setBaseContext: async (base: BaseContext, userDbName: string | null, workerDbName: string | null) => {
            await get().switchContext({ base }, userDbName, workerDbName);
        },

        setExtendedContext: async (extended: ExtendedContext, userDbName: string | null, workerDbName: string | null) => {
            await get().switchContext({ extended }, userDbName, workerDbName);
        },

        navigate: async (nodeId: string, _dbName: string) => {
            try {
                set({ isLoading: true, error: null });
                if (!get()._accessToken) { set({ isLoading: false }); return; }

                const currentState = get().context;
                const existingIndex = currentState.history.nodes.findIndex(n => n.id === nodeId);
                if (existingIndex !== -1) {
                    const newHistory = navigateHistory(currentState.history, existingIndex);
                    set({ context: { ...currentState, node: getCurrentHistoryNode(newHistory), history: newHistory }, isLoading: false });
                    return;
                }

                const rooms = await pgFetch<WhiteboardRoom[]>('GET', 'whiteboard_rooms', {
                    query: `id=eq.${nodeId}&user_id=eq.${get()._userId}`,
                });
                if (!rooms || rooms.length === 0) throw new Error(`Whiteboard room not found: ${nodeId}`);

                const room = rooms[0];
                const node: NavigationNode = {
                    id: room.id,
                    node_storage_path: room.storage_path || `${get()._userId}/workspaces/${room.context_type}_default.json`,
                    label: room.name,
                    type: 'workspace',
                };
                const newHistory = addToHistory(currentState.history, node);
                set({ context: { ...currentState, node, history: newHistory }, isLoading: false });
            } catch (error) {
                logger.error('navigation', '❌ navigate failed', error);
                set({ error: error instanceof Error ? error.message : 'Failed to navigate', isLoading: false });
            }
        },

        navigateToNode: async (node: NavigationNode, userDbName: string | null, workerDbName: string | null) => {
            if (!isValidNodeType(node.type)) {
                logger.warn('navigation', `⚠️ navigateToNode called with non-graph type: ${node.type} — navigating anyway`);
            }
            await get().navigate(node.id, userDbName || '');
        },

        refreshNavigationState: async (_userDbName: string | null, _workerDbName: string | null) => {
            try {
                set({ isLoading: true, error: null });
                const currentNode = get().context.node;
                if (currentNode && get()._accessToken) {
                    const rooms = await pgFetch<WhiteboardRoom[]>('GET', 'whiteboard_rooms', {
                        query: `id=eq.${currentNode.id}`,
                    });
                    if (rooms && rooms.length > 0) {
                        const room = rooms[0];
                        set({
                            context: {
                                ...get().context,
                                node: {
                                    id: room.id,
                                    node_storage_path: room.storage_path || currentNode.node_storage_path,
                                    label: room.name,
                                    type: 'workspace',
                                },
                            },
                        });
                    }
                }
                set({ isLoading: false });
            } catch (error) {
                logger.error('navigation', '❌ refreshNavigationState failed', error);
                set({ error: error instanceof Error ? error.message : 'Failed to refresh', isLoading: false });
            }
        },

        navigateToNeoNode: async (neoNode: NeoGraphNode) => {
            const userId = get()._userId;
            if (!userId || !get()._accessToken) {
                logger.warn('navigation', '⚠️ navigateToNeoNode called without auth');
                return;
            }
            try {
                set({ isLoading: true, error: null });
                const existing = await pgFetch<WhiteboardRoom[]>('GET', 'whiteboard_rooms', {
                    query: `user_id=eq.${userId}&neo4j_node_id=eq.${neoNode.neo4j_node_id}`,
                });
                let room: WhiteboardRoom;
                if (existing && existing.length > 0) {
                    room = existing[0];
                } else {
                    const storagePath = `${userId}/nodes/${neoNode.neo4j_node_id}.json`;
                    const created = await pgFetch<WhiteboardRoom>('POST', 'whiteboard_rooms', {
                        body: {
                            user_id: userId,
                            name: neoNode.label,
                            context_type: neoNode.node_type.toLowerCase(),
                            is_default: false,
                            storage_path: storagePath,
                            neo4j_node_id: neoNode.neo4j_node_id,
                            neo4j_db_name: neoNode.neo4j_db_name,
                            node_type: neoNode.node_type,
                        },
                        prefer: 'return=representation',
                        single: true,
                    });
                    if (!created) throw new Error('Failed to create whiteboard room for node');
                    room = created;
                }
                const node: NavigationNode = {
                    id: room.id,
                    node_storage_path: room.storage_path || `${userId}/nodes/${neoNode.neo4j_node_id}.json`,
                    label: room.name,
                    type: neoNode.node_type,
                };
                const currentState = get().context;
                const newHistory = addToHistory(currentState.history, node);
                set({ context: { ...currentState, node, history: newHistory }, isLoading: false, error: null });
                logger.debug('navigation', '✅ Navigated to Neo4j node', { neoNode });
            } catch (error) {
                logger.error('navigation', '❌ navigateToNeoNode failed', error);
                set({ error: error instanceof Error ? error.message : 'Navigation failed', isLoading: false });
            }
        },
    };
});
