import axiosInstance from '../../axiosConfig';
import { formatEmailForDatabase } from './neoDBService';
import { CCUserNodeProps, CCTeacherNodeProps, CCCalendarNodeProps, CCUserTeacherTimetableNodeProps } from '../../utils/tldraw/cc-base/cc-graph/cc-graph-types';
import { NavigationNode, NodeContext } from '../../types/navigation';
import { TLBinding, TLShapeId } from '@tldraw/tldraw';
import { logger } from '../../debugConfig';
import { useNavigationStore } from '../../stores/navigationStore';
import { DatabaseNameService } from './databaseNameService';

// Dev configuration - only hardcoded value we need
const DEV_SCHOOL_NAME = 'default';
const DEV_SCHOOL_GROUP = 'development'

const ADMIN_USER_NAME = 'kcar';
const ADMIN_USER_GROUP = 'admin';

interface ShapeState {
    parentId: TLShapeId | null;
    isPageChild: boolean | null;
    hasChildren: boolean | null;
    bindings: TLBinding[] | null;
}

interface NodeResponse {
    status: string;
    nodes: {
        userNode: CCUserNodeProps;
        calendarNode: CCCalendarNodeProps;
        teacherNode: CCTeacherNodeProps;
        timetableNode: CCUserTeacherTimetableNodeProps;
    };
}

interface NodeDataResponse {
    __primarylabel__: string;
    uuid_string: string;
    node_storage_path: string;
    created: string;
    merged: string;
    state: ShapeState | null;
    defaultComponent: boolean | null;
    user_name?: string;
    user_email?: string;
    user_type?: string;
    user_id?: string;
    worker_node_data?: string;
    [key: string]: string | number | boolean | null | ShapeState | undefined;
}

interface DefaultNodeResponse {
    status: string;
    node: {
    id: string;
    node_storage_path: string;
    type: string;
    label: string;
        data: NodeDataResponse;
    };
}

export interface ProcessedUserNodes {
    privateUserNode: CCUserNodeProps;
    connectedNodes: {
        calendar?: CCCalendarNodeProps;
        teacher?: CCTeacherNodeProps;
        timetable?: CCUserTeacherTimetableNodeProps;
    };
}

export interface CalendarStructureResponse {
  status: string;
  data: {
    currentDay: string;
    days: Record<string, {
      id: string;
      date: string;
      title: string;
    }>;
    weeks: Record<string, {
      id: string;
      title: string;
      days: { id: string }[];
      startDate: string;
      endDate: string;
    }>;
    months: Record<string, {
      id: string;
      title: string;
      days: { id: string }[];
      weeks: { id: string }[];
      year: string;
      month: string;
    }>;
    years: {
      id: string;
      title: string;
      months: { id: string }[];
      year: string;
    }[];
  };
}

export interface WorkerStructureResponse {
  status: string;
  data: {
    timetables: Record<string, Array<{
      id: string;
      title: string;
      type: string;
      startTime: string;
      endTime: string;
    }>>;
    classes: Record<string, Array<{
      id: string;
      title: string;
      type: string;
    }>>;
    lessons: Record<string, Array<{
      id: string;
      title: string;
      type: string;
    }>>;
    journals: Record<string, Array<{
      id: string;
      title: string;
    }>>;
    planners: Record<string, Array<{
      id: string;
      title: string;
    }>>;
  };
}

export class UserNeoDBService {
    static async fetchUserNodesData(
        email: string,
        userDbName?: string,
        workerDbName?: string
    ): Promise<ProcessedUserNodes | null> {
        try {
            if (!userDbName) {
                logger.error('neo4j-service', '❌ Attempted to fetch nodes without database name');
                return null;
            }

            const formattedEmail = formatEmailForDatabase(email);
            const uniqueId = `User_${formattedEmail}`;
            
            logger.debug('neo4j-service', '🔄 Fetching user nodes data', { 
                email,
                formattedEmail,
                userDbName,
                workerDbName,
                uniqueId 
            });

            // First get the user node from profile context
            const userNode = await this.getDefaultNode('profile', userDbName);
            if (!userNode || !userNode.data) {
                throw new Error('Failed to fetch user node or node data missing');
            }

            logger.debug('neo4j-service', '✅ Found user node', {
                nodeId: userNode.id,
                type: userNode.type,
                hasData: !!userNode.data,
                userDbName,
                workerDbName
            });

            // Initialize result structure
            const processedNodes: ProcessedUserNodes = {
                privateUserNode: {
                    ...userNode.data,
                    __primarylabel__: 'User' as const,
                    title: userNode.data.user_email || 'User',
                    w: 200,
                    h: 200,
                    headerColor: '#3e6589',
                    backgroundColor: '#f0f0f0',
                    isLocked: false
                } as CCUserNodeProps,
                connectedNodes: {}
            };

            try {
                // Get calendar node from calendar context
                const calendarNode = await this.getDefaultNode('calendar', userDbName);
                if (calendarNode?.data) {
                    processedNodes.connectedNodes.calendar = {
                        ...calendarNode.data,
                        __primarylabel__: 'Calendar' as const,
                        title: calendarNode.data.calendar_name || 'Calendar',
                        w: 200,
                        h: 200,
                        headerColor: '#3e6589',
                        backgroundColor: '#f0f0f0',
                        isLocked: false
                    } as CCCalendarNodeProps;
                    logger.debug('neo4j-service', '✅ Found calendar node', {
                        nodeId: calendarNode.id,
                        node_storage_path: calendarNode.data.node_storage_path
                    });
                } else {
                    logger.debug('neo4j-service', 'ℹ️ No calendar node found');
                }
            } catch (error) {
                logger.warn('neo4j-service', '⚠️ Failed to fetch calendar node:', error);
                // Continue without calendar node
            }

            // Get teacher node from teaching context if worker database is available
            if (workerDbName) {
                try {
                    const teacherNode = await this.getDefaultNode('teaching', userDbName);
                    if (teacherNode?.data) {
                        processedNodes.connectedNodes.teacher = {
                            ...teacherNode.data,
                            __primarylabel__: 'Teacher' as const,
                            title: teacherNode.data.teacher_name_formal || 'Teacher',
                            w: 200,
                            h: 200,
                            headerColor: '#3e6589',
                            backgroundColor: '#f0f0f0',
                            isLocked: false,
                            user_db_name: userDbName,
                            school_db_name: workerDbName
                        } as CCTeacherNodeProps;
                        logger.debug('neo4j-service', '✅ Found teacher node', {
                            nodeId: teacherNode.id,
                            node_storage_path: teacherNode.data.node_storage_path,
                            userDbName,
                            workerDbName
                        });
                    } else {
                        logger.debug('neo4j-service', 'ℹ️ No teacher node found');
                    }
                } catch (error) {
                    logger.warn('neo4j-service', '⚠️ Failed to fetch teacher node:', error);
                    // Continue without teacher node
                }
            }

            logger.debug('neo4j-service', '✅ Processed all user nodes', {
                hasUserNode: !!processedNodes.privateUserNode,
                hasCalendar: !!processedNodes.connectedNodes.calendar,
                hasTeacher: !!processedNodes.connectedNodes.teacher,
                teacherData: processedNodes.connectedNodes.teacher ? {
                    uuid_string: processedNodes.connectedNodes.teacher.uuid_string,
                    school_db_name: processedNodes.connectedNodes.teacher.school_db_name,
                    node_storage_path: processedNodes.connectedNodes.teacher.node_storage_path
                } : null
            });

            return processedNodes;
        } catch (error: unknown) {
            if (error instanceof Error) {
                logger.error('neo4j-service', '❌ Failed to fetch user nodes:', error.message);
            } else {
                logger.error('neo4j-service', '❌ Failed to fetch user nodes:', String(error));
            }
            throw error;
        }
    }

    static getUserDatabaseName(userType: string, identifier: string): string {
        return DatabaseNameService.getUserPrivateDB(userType, identifier);
    }

    static getSchoolDatabaseName(schoolId: string): string {
        return DatabaseNameService.getSchoolPrivateDB(schoolId);
    }

    static getDefaultSchoolDatabaseName(): string {
        return DatabaseNameService.getStoredSchoolDatabase() || '';
    }

    static async fetchNodeData(nodeId: string, dbName: string): Promise<{ node_type: string; node_data: NodeDataResponse } | null> {
        try {
            logger.debug('neo4j-service', '🔄 Fetching node data', { nodeId, dbName });

            const response = await axiosInstance.get<{
                status: string;
                node: {
                    node_type: string;
                    node_data: NodeDataResponse;
                };
            }>('/database/tools/get-node', {
                params: { 
                    uuid_string: nodeId,
                    db_name: dbName
                }
            });
            
            if (response.data?.status === 'success' && response.data.node) {
                return response.data.node;
            }
            
            return null;
        } catch (error) {
            logger.error('neo4j-service', '❌ Failed to fetch node data:', error);
            throw error;
        }
    }

    static getNodeDatabaseName(node: NavigationNode): string {
        // Validate that node and node_storage_path exist
        if (!node || !node.node_storage_path) {
            logger.error('neo4j-service', '❌ Invalid node or missing node_storage_path', { 
                node: node ? { id: node.id, type: node.type, label: node.label } : null,
                hasStoragePath: !!node?.node_storage_path
            });
            throw new Error('Node is missing required storage path information');
        }

        // If the node path starts with /node_filesystem/users/, it's in a user database
        if (node.node_storage_path.startsWith('users/')) {
            const parts = node.node_storage_path.split('/');
            const databaseIndex = parts.indexOf('databases');
            if (databaseIndex >= 0 && parts.length > databaseIndex + 1) {
                return parts[databaseIndex + 1];
            }
            // parts[3] should be the database name (e.g., cc.users.surfacedashdev3atkevlaraidotcom)
            if (parts.length >= 4) {
                return parts[3];
            }
            logger.warn('neo4j-service', '⚠️ Unexpected user path format', { path: node.node_storage_path });
            return 'cc.users';
        }
        
        // For Supabase Storage paths (cc.public.snapshots/...), determine database based on node type
        if (node.node_storage_path.startsWith('cc.public.snapshots/')) {
            const parts = node.node_storage_path.split('/');
            const nodeType = parts[1]; // e.g., 'User', 'Teacher', 'School'
            
            if (nodeType === 'User') {
                return DatabaseNameService.getStoredUserDatabase() || 'cc.users';
            } else if (nodeType === 'Teacher' || nodeType === 'Student') {
                return DatabaseNameService.getStoredSchoolDatabase() || 'cc.institutes';
            } else if (nodeType === 'School') {
                return DatabaseNameService.getStoredSchoolDatabase() || 'cc.institutes';
            }
        }
        
        // For school/worker nodes, extract from the path or use a default
        if (node.node_storage_path.startsWith('schools/')) {
            const parts = node.node_storage_path.split('/');
            const databaseIndex = parts.indexOf('databases');
            if (databaseIndex >= 0 && parts.length > databaseIndex + 1) {
                return parts[databaseIndex + 1];
            }
            if (parts.length >= 4) {
                return parts[3];
            }
            const storedSchoolDb = DatabaseNameService.getStoredSchoolDatabase();
            if (storedSchoolDb) {
                logger.warn('neo4j-service', '⚠️ Falling back to stored school database name', {
                    path: node.node_storage_path,
                    storedSchoolDb
                });
                return storedSchoolDb;
            }
            logger.warn('neo4j-service', '⚠️ Could not determine school database from path', { path: node.node_storage_path });
            return DatabaseNameService.getStoredSchoolDatabase() || 'cc.institutes';
        }
        
        // Try to extract from path, but provide fallback
        const parts = node.node_storage_path.split('/');
        if (parts.length >= 4) {
            return parts[3];
        }
        
        // Default fallback
        logger.warn('neo4j-service', '⚠️ Using fallback database name', { 
            path: node.node_storage_path,
            nodeType: node.type 
        });
        return 'cc.users'; //TODO: remove hard-coding
    }

    static async getDefaultNode(context: NodeContext, dbName: string): Promise<NavigationNode | null> {
        try {
            logger.debug('neo4j-service', '🔄 Fetching default node', { context, dbName });
            
            // For overview context, we need to extract the base context from the current navigation state
            const params: Record<string, string> = { db_name: dbName };
            if (context === 'overview') {
                // Get the current base context from the navigation store
                const navigationStore = useNavigationStore.getState();
                params.base_context = navigationStore.context.base;
            }
            
            const response = await axiosInstance.get<DefaultNodeResponse>(
                `/database/tools/get-default-node/${context}`,
                { params }
            );
            
            if (response.data?.status === 'success' && response.data.node) {
                return {
                    id: response.data.node.id,
                    node_storage_path: response.data.node.node_storage_path,
                    type: response.data.node.type,
                    label: response.data.node.label,
                    data: response.data.node.data
                };
            }
            
            return null;
        } catch (error) {
            logger.error('neo4j-service', '❌ Failed to fetch default node:', error);
            throw error;
        }
    }

    static async fetchCalendarStructure(dbName: string): Promise<CalendarStructureResponse['data']> {
        try {
            logger.debug('navigation', '🔄 Fetching calendar structure', { dbName });
            
            const response = await axiosInstance.get<CalendarStructureResponse>(
                `/database/calendar-structure/get-calendar-structure?db_name=${dbName}`
            );

            if (response.data.status === 'success') {
                logger.info('navigation', '✅ Calendar structure fetched successfully');
                return response.data.data;
            }

            throw new Error('Failed to fetch calendar structure');
        } catch (error) {
            logger.error('navigation', '❌ Failed to fetch calendar structure:', error);
            throw error;
        }
    }

    static async fetchWorkerStructure(dbName: string): Promise<WorkerStructureResponse['data']> {
        try {
            logger.debug('navigation', '🔄 Fetching worker structure', { dbName });
            
            const response = await axiosInstance.get<WorkerStructureResponse>(
                `/database/worker-structure/get-worker-structure?db_name=${dbName}`
            );

            if (response.data.status === 'success') {
                logger.info('navigation', '✅ Worker structure fetched successfully');
                return response.data.data;
            }

            throw new Error('Failed to fetch worker structure');
        } catch (error) {
            logger.error('navigation', '❌ Failed to fetch worker structure:', error);
            throw error;
        }
    }
}
