import {
    Editor,
    HTMLContainer,
    Rectangle2d,
    ShapeUtil,
    TLDefaultColorTheme,
    getDefaultColorTheme,
    createShapeId
} from 'tldraw'
import {
    AllNodeShapes
} from './graph-shape-types'
import {
    AllRelationshipShapes
} from './graph-relationship-types'
import { getNodeComponent } from './nodeComponents';
import axios from '../../../axiosConfig';
import graphState from './graphStateUtil';

export const nodeTypeConfig = {
    Developer: { shapeType: 'developer_node', color: 'light-blue' },
    Teacher: { shapeType: 'teacher_node', color: 'light-green' },
    
    User: { shapeType: 'user_node', color: 'light-green' },
    TeacherTimetable: { shapeType: 'teacher_timetable_node', color: 'blue' },
    TimetableLesson: { shapeType: 'timetable_lesson_node', color: 'light-blue' },
    PlannedLesson: { shapeType: 'planned_lesson_node', color: 'light-green' },
    School: { shapeType: 'school_node', color: 'grey' },
    Calendar: { shapeType: 'calendar_node', color: 'violet' },
    CalendarYear: { shapeType: 'calendar_year_node', color: 'red' },
    CalendarMonth: { shapeType: 'calendar_month_node', color: 'light-violet' },
    CalendarWeek: { shapeType: 'calendar_week_node', color: 'light-red' },
    CalendarDay: { shapeType: 'calendar_day_node', color: 'light-blue' },
    CalendarTimeChunk: { shapeType: 'calendar_time_chunk_node', color: 'blue' },
    ScienceLab: { shapeType: 'science_lab_node', color: 'yellow' },
    KeyStageSyllabus: { shapeType: 'key_stage_syllabus_node', color: 'grey' },
    YearGroupSyllabus: { shapeType: 'year_group_syllabus_node', color: 'light-blue' },
    CurriculumStructure: { shapeType: 'curriculum_structure_node', color: 'grey' },
    Topic: { shapeType: 'topic_node', color: 'green' },
    TopicLesson: { shapeType: 'topic_lesson_node', color: 'light-green' },
    LearningStatement: { shapeType: 'learning_statement_node', color: 'light-blue' },
    SchoolTimetable: { shapeType: 'school_timetable_node', color: 'grey' },
    AcademicYear: { shapeType: 'academic_year_node', color: 'light-violet' },
    AcademicTerm: { shapeType: 'academic_term_node', color: 'yellow' },
    AcademicWeek: { shapeType: 'academic_week_node', color: 'orange' },
    AcademicDay: { shapeType: 'academic_day_node', color: 'light-red' },
    AcademicPeriod: { shapeType: 'academic_period_node', color: 'light-green' },
    RegistrationPeriod: { shapeType: 'registration_period_node', color: 'light-green' },
    PastoralStructure: { shapeType: 'pastoral_structure_node', color: 'grey' },
    KeyStage: { shapeType: 'key_stage_node', color: 'blue' },
    Department: { shapeType: 'department_node', color: 'light-blue' },
    Room: { shapeType: 'room_node', color: 'violet' },
    SubjectClass: { shapeType: 'subject_class_node', color: 'light-blue' },
};

const createNodeComponent = (shape: AllNodeShapes, theme: TLDefaultColorTheme, editor: Editor) => {
    let isDragging = false;
    let startX = 0;
    let startY = 0;

    const borderColor = theme.id === 'dark' ? 'white' : 'black'

    const handlePointerDown = (e: React.PointerEvent) => {
        e.preventDefault()
        e.stopPropagation()
        
        const rect = e.currentTarget.getBoundingClientRect()
        const x = e.clientX - rect.left
        const y = e.clientY - rect.top

        // Define button areas
        const openFileButtonArea = { x: 10, y: shape.props.h - 60, width: shape.props.w - 20, height: 25 }
        const getConnectedNodesButtonArea = { x: 10, y: shape.props.h - 30, width: shape.props.w - 20, height: 25 }

        if (isPointInRect(x, y, openFileButtonArea)) {
            console.log('Clicked on Open File button')
            loadTldrawFile(shape.props.path, editor)
        } else if (isPointInRect(x, y, getConnectedNodesButtonArea)) {
            console.log('Clicked on Get Connected Nodes button')
            handleGetConnectedNodes()
        } else if (isPointInShape(x, y, shape) && !isPointInRect(x, y, openFileButtonArea) && !isPointInRect(x, y, getConnectedNodesButtonArea)) {
            console.log('Clicked on shape')
            isDragging = true;
            startX = e.clientX - shape.x;
            startY = e.clientY - shape.y;
        }
    }

    const handlePointerMove = (e: React.PointerEvent) => {
        if (isDragging) {
            const newX = e.clientX - startX;
            const newY = e.clientY - startY;
            editor.updateShape({
                id: shape.id,
                type: shape.type,
                x: newX,
                y: newY,
            });
        }
    }

    const handlePointerUp = (e: React.PointerEvent) => {
        isDragging = false;
    }

    const isPointInShape = (x: number, y: number, shape: AllNodeShapes) => {
        const bounds = editor.getShapeGeometry(shape).bounds
        return x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height
    }

    const isPointInRect = (x: number, y: number, rect: { x: number, y: number, width: number, height: number }) => {
        return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height
    }

    let isFetchingConnectedNodes = false;

    const handleGetConnectedNodes = async () => {
        if (isFetchingConnectedNodes) {
            console.log("WARNING! Already fetching connected nodes. Skipping...");
            return;
        }
        isFetchingConnectedNodes = true;
    
        console.log("Getting connected nodes for:", shape.props.uuid_string);
        try {
            const response = await axios.get(`/api/database/tools/get-connected-nodes-and-edges?uuid_string=${shape.props.uuid_string}`);
            console.log("Connected nodes response:", response.data);
            if (response.data.status === "success") {
                const mainNode = response.data.main_node;
                const connectedNodes = response.data.connected_nodes;
                const relationships = response.data.relationships;

                // Add nodes to the graph
                [mainNode, ...connectedNodes].forEach((node: any) => {
                    console.log("Node:", node);
                    const newShapeId = createShapeId(node.node_data.uuid_string);
                    const doesShapeExist = editor.getShape(newShapeId);
                    if (!doesShapeExist) {
                        console.log("Creating new shape with ID:", newShapeId);
                        const nodeConfig = nodeTypeConfig[node.node_type as keyof typeof nodeTypeConfig];
                        if (nodeConfig) {
                            const newShape = {
                                id: newShapeId,
                                type: nodeConfig.shapeType,
                                x: 0,
                                y: 0,
                                props: {
                                    color: nodeConfig.color,
                                    ...node.node_data
                                }
                            };
                            console.log("New shape:", newShape);
                            console.log("Creating shape:", newShape);
                            editor.createShape(newShape);
                            console.log("New shape created:", newShape);
                            const bounds = editor.getShapeGeometry(newShapeId).bounds;
                            console.log("Shape bounds:", bounds);
                            console.log("Updating shape with width:", bounds.w, "and height:", bounds.h);
                            newShape.props.w = bounds.w;
                            newShape.props.h = bounds.h;
                            console.log("Adding node to graphState:", newShape);
                            const shapeWithWidthAndHeight = {
                                ...newShape,
                                w: bounds.w,
                                h: bounds.h
                            }
                            graphState.addNode(shapeWithWidthAndHeight);
                            console.log("Node added to graphState:", newShape);
                        } else {
                            console.log("WARNING! Node type not found:", node.node_type);
                        }
                    }
                });
                console.log("Updating shapes with dagre...");
                graphState.setEditor(editor);
                graphState.updateShapesWithDagre();

                // Add edges to the graph
                relationships.forEach((relationship: any) => {
                    graphState.addEdge(relationship.start_node.uuid_string, relationship.end_node.uuid_string);
                });

                // Create edge shapes
                graphState.getEdges().forEach((edge: any) => {
                    console.log("WARNING! Cancelling createEdgeComponent()...");
                    // console.log("handleGetConnectedNodes(): Creating edge component for:", edge.v, edge.w);
                    // createEdgeComponent(edge.v, edge.w, editor);
                });

                console.log("Done!");
            } else {
                console.error('Error in response:', response.data.message);
            }
        } catch (error) {
            console.error('Error fetching connected nodes:', error);
        } finally {
            isFetchingConnectedNodes = false;
        }
    };

    const loadTldrawFile = async (path: string, editor: any) => {
        console.log("Loading tldraw_file...")
        try {
            const response = await axios.get(`/api/database/tldraw_fs/get_tldraw_user_file${path}/tldraw_file.json`);
            const fileContent = response.data;
            
            console.log("File content:", fileContent);
    
            if (fileContent && fileContent.document && fileContent.document.store) {
    
                // Ensure the schema version is set
                if (!fileContent.document.schema) {
                    console.log("!fileContent.document.schema")
                    fileContent.document.schema = { schemaVersion: 1 };
                } else if (!fileContent.document.schema.schemaVersion) {
                    console.log("!fileContent.document.schema.schemaVersion")
                    fileContent.document.schema.schemaVersion = 1;
                }
    
                // Load the new content
                console.log("Loading snapshot: ", fileContent)
                editor.loadSnapshot(fileContent);
            } else {
                console.error('Invalid file content structure:', fileContent);
                throw new Error('Invalid file content structure');
            }
        } catch (error) {
            console.error('Error loading tldraw file:', error);
        }
    };

    return (
        <HTMLContainer
            id={shape.id}
            style={{
                border: `1px solid ${borderColor}`,
                borderRadius: '5px',
                display: 'flex',
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'space-between',
                pointerEvents: 'all',
                backgroundColor: theme[shape.props.color].semi,
                color: theme[shape.props.color].solid,
                boxShadow: '1px 1px 2px rgba(0, 0, 0, 0.1)',
                transition: 'all 0.3s ease',
                overflow: 'hidden',
                padding: '10px',
                height: shape.props.h,
            }}
            onPointerDown={handlePointerDown}
            onPointerMove={handlePointerMove}
            onPointerUp={handlePointerUp}
        >
            {getNodeComponent(shape, theme)}
            <div style={{ display: 'flex', flexDirection: 'column', width: '100%', marginTop: '10px' }}>
                <div
                    style={{
                        backgroundColor: theme[shape.props.color].solid,
                        color: theme[shape.props.color].semi,
                        padding: '5px',
                        borderRadius: '3px',
                        cursor: 'pointer',
                        marginBottom: '5px',
                        textAlign: 'center',
                    }}
                    onClick={() => loadTldrawFile(shape.props.path, editor)}
                >
                    Open File
                </div>
                <div
                    style={{
                        backgroundColor: theme[shape.props.color].solid,
                        color: theme[shape.props.color].semi,
                        padding: '5px',
                        borderRadius: '3px',
                        cursor: 'pointer',
                        textAlign: 'center',
                    }}
                    onClick={handleGetConnectedNodes}
                >
                    Get Connected Nodes
                </div>
            </div>
        </HTMLContainer>
    )
}

const createNodeIndicator = (shape: AllNodeShapes, editor: any) => {
    const bounds = editor.getShapeGeometry(shape).bounds
    const theme = getDefaultColorTheme({ isDarkMode: editor.user.getIsDarkMode() })
        return (
            <rect
                x={0}
                y={0}
                width={bounds.width}
                height={bounds.height}
                fill="none"
                stroke={theme[shape.props.color].solid}
                strokeWidth={2}
                rx={5}
                ry={5}
            />
        )
}

const createEdgeComponent = (sourceId: string, targetId: string, editor: any) => {
    console.log("Creating edge component for:", sourceId, targetId)
    const edge = {
        type: 'general_relationship',
        props: {
            w: 200,
            h: 300,
            color: 'black',
            __relationshiptype__: '',
            source: sourceId,
            target: targetId,
        }
    };
    editor.createShape(edge);
    graphState.addNode(edge);
};

export abstract class BaseNodeShapeUtil<T extends AllNodeShapes> extends ShapeUtil<T> {
    static override type: string

    static override props: any
    static override migrations: any

    override isAspectRatioLocked = (_shape: T) => true
    override canResize = (_shape: T) => true

    abstract override getDefaultProps(): T['props']

    getGeometry(shape: T) {
        return new Rectangle2d({
            width: shape.props.w,
            height: shape.props.h,
            x: 0,
            y: 0,
            isFilled: true,
        })
    }

    component(shape: T) {
        const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
        return createNodeComponent(shape, theme, this.editor)
    }

    indicator(shape: T) {
        return createNodeIndicator(shape, this.editor)
    }

    onDrag = (shape: T, dx: number, dy: number) => {
        return {
            x: shape.x + dx,
            y: shape.y + dy,
        }
    }
}

export abstract class BaseRelationshipShapeUtil<T extends AllRelationshipShapes> extends ShapeUtil<T> {
    static override type: string

    static override props: any
    static override migrations: any

    override isAspectRatioLocked = (_shape: T) => true
    override canResize = (_shape: T) => true

    abstract override getDefaultProps(): T['props']

    getGeometry(shape: T) {
        return new Rectangle2d({
            width: shape.props.w,
            height: shape.props.h,
            x: 0,
            y: 0,
            isFilled: true,
        });
    }

    component(shape: T) {
        // Define how the edge is rendered
        return (
            <line
                x1={shape.x}
                y1={shape.y}
                x2={shape.x}
                y2={shape.y}
                stroke={shape.props.color}
                strokeWidth={2}
            />
        );
    }

    indicator(shape: T) {
        // Define the indicator for the edge
        return (
            <line
                x1={shape.x}
                y1={shape.y}
                x2={shape.x}
                y2={shape.y}
                stroke={shape.props.color}
                strokeWidth={2}
                strokeDasharray="4 2"
            />
        );
    }
}