import { Editor, createShapeId, IndexKey } from '@tldraw/tldraw';
import axios from '../../axiosConfig';
import { getShapeType, isValidNodeType, CCNodeTypes } from '../../utils/tldraw/cc-base/cc-graph/cc-graph-types';
import { AllNodeShapes, NodeShapeType, ShapeUtils } from '../../utils/tldraw/cc-base/cc-graph/cc-graph-shapes';
import { graphState } from '../../utils/tldraw/cc-base/cc-graph/graphStateUtil';
import { NodeResponse, ConnectedNodesResponse } from '../../types/api';
import { logger } from '../../debugConfig';

export class GraphNeoDBService {
    static async fetchConnectedNodesAndEdges(
        uuid_string: string,
        db_name: string,
        editor: Editor
    ) {
        try {
            logger.debug('graph-service', '📤 Fetching connected nodes', { 
                uuid_string,
                db_name 
            });

            const response = await axios.get<ConnectedNodesResponse>(
                '/database/tools/get-connected-nodes-and-edges', {
                    params: { 
                        uuid_string,
                        db_name
                    }
                }
            );

            if (response.data.status === "success") {
                // Make sure editor is set in graphState
                graphState.setEditor(editor);
                await this.processConnectedNodesResponse(response.data);
                return true;
            }
            
            throw new Error('Failed to fetch connected nodes');
        } catch (error) {
            logger.error('graph-service', '❌ Failed to fetch connected nodes', { error });
            throw error;
        }
    }

    private static async processConnectedNodesResponse(
        data: ConnectedNodesResponse
    ) {
        try {
            // Log the incoming data
            logger.debug('graph-service', '📥 Processing nodes response', {
                mainNode: data.main_node,
                connectedNodesCount: data.connected_nodes?.length,
                relationshipsCount: data.relationships?.length
            });

            // Create a batch of nodes to process
            const nodesToProcess: NodeResponse['node_data'][] = [];

            // Add connected nodes first
            if (data.connected_nodes) {
                data.connected_nodes.forEach(connectedNode => {
                    if (isValidNodeType(connectedNode.type)) {
                        // Convert the simplified node structure to node_data format
                        const nodeData = {
                            uuid_string: connectedNode.id,
                            node_storage_path: connectedNode.node_storage_path,
                            name: connectedNode.label,
                            __primarylabel__: connectedNode.type as keyof CCNodeTypes,
                            created: new Date().toISOString(),
                            merged: new Date().toISOString(),
                        };
                        nodesToProcess.push(nodeData);
                    }
                });
            }

            // Add main node last (if it exists) to ensure it's processed after connected nodes
            if (data.main_node) {
                nodesToProcess.push(data.main_node.node_data);
            }

            // Process all nodes in batch
            for (const nodeData of nodesToProcess) {
                await this.createOrUpdateNode(nodeData);
                logger.debug('graph-service', '📝 Processed node', { 
                    nodeId: nodeData.uuid_string,
                    nodeType: nodeData.__primarylabel__
                });
            }

            // After all nodes are processed, arrange them in grid
            graphState.arrangeNodesInGrid();

            logger.debug('graph-service', '✅ Processed nodes batch', { 
                processedCount: nodesToProcess.length,
                totalNodes: graphState.getAllNodes().length,
                nodesInState: Array.from(graphState.nodeData.keys())
            });

        } catch (error) {
            logger.error('graph-service', '❌ Failed to process connected nodes response', { error });
            throw error;
        }
    }

    private static async createOrUpdateNode(
        nodeData: NodeResponse['node_data']
    ) {
        const uniqueId = nodeData.uuid_string;
        const nodeType = nodeData.__primarylabel__;

        if (!isValidNodeType(nodeType)) {
            logger.warn('graph-service', '⚠️ Unknown node type', { data: nodeData });
            return;
        }

        const shapeType = getShapeType(nodeType) as NodeShapeType;
        
        // Get the shape util for this node type
        const shapeUtil = ShapeUtils[shapeType];
        if (!shapeUtil) {
            logger.warn('graph-service', '⚠️ No shape util found for type', { type: shapeType });
            return;
        }

        // Get default props from the shape util's prototype
        const defaultProps = shapeUtil.prototype.getDefaultProps();

        // Create the shape with proper typing based on the node type
        // Filter out properties that TLDraw doesn't expect
        const { path, cc_username, user_db_name, ...filteredNodeData } = nodeData;
        
        // Map backend properties to TLDraw shape properties
        const mappedProps = {
            ...defaultProps,
            ...filteredNodeData,
            __primarylabel__: nodeData.__primarylabel__,
            uuid_string: nodeData.uuid_string,
            node_storage_path: nodeData.node_storage_path as string || '',
        };

        // Add missing properties for cc-user-node
        if (shapeType === 'cc-user-node') {
            mappedProps.user_id = nodeData.uuid_string; // Use uuid_string as user_id
            mappedProps.worker_node_data = JSON.stringify({
                cc_username: nodeData.cc_username || '',
                user_db_name: nodeData.user_db_name || ''
            });
        }
        
        const shape = {
            id: createShapeId(uniqueId),
            type: shapeType,
            x: 0,
            y: 0,
            rotation: 0,
            index: 'a1' as IndexKey,
            parentId: createShapeId('page:page'),
            isLocked: false,
            opacity: 1,
            meta: {},
            props: mappedProps
        };

        // Add to graphState
        graphState.addNode(shape as AllNodeShapes);

        logger.debug('graph-service', '📝 Node processed', { 
            uniqueId,
            nodeType,
            shapeId: shape.id,
            shapeType: shape.type
        });
    }
}
