import { useEffect, useRef } from "react";
import { observer } from "mobx-react-lite";
import { runInAction } from "mobx";
import * as Blockly from "blockly";
import type { ObjectType } from "../../types";
import './ScriptEditorView.scss';

const TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = {
    kind: 'flyoutToolbox',
    contents: [
        { kind: 'block', type: 'controls_if' },
        { kind: 'block', type: 'controls_repeat_ext' },
        { kind: 'block', type: 'logic_compare' },
        { kind: 'block', type: 'math_number' },
        { kind: 'block', type: 'math_arithmetic' },
        { kind: 'block', type: 'text' },
        { kind: 'block', type: 'text_print' },
    ],
};

type ScriptEditorViewProps = {
    objectType: ObjectType;
}

export const ScriptEditorView = observer(function ({ objectType }: ScriptEditorViewProps) {
    const containerRef = useRef<HTMLDivElement>(null);
    const workspaceRef = useRef<Blockly.WorkspaceSvg | null>(null);
    const isLoadingRef = useRef(false);

    useEffect(() => {
        if (!containerRef.current) return;

        const workspace = Blockly.inject(containerRef.current, { toolbox: TOOLBOX });
        workspaceRef.current = workspace;

        if (objectType.program) {
            try {
                isLoadingRef.current = true;
                Blockly.serialization.workspaces.load(JSON.parse(objectType.program), workspace);
            } catch {
                // corrupt program, start fresh
            } finally {
                isLoadingRef.current = false;
            }
        }

        const listener = (e: Blockly.Events.Abstract) => {
            if (isLoadingRef.current || e.isUiEvent) return;
            const serialized = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
            runInAction(() => { objectType.program = serialized; });
        };
        workspace.addChangeListener(listener);

        const resizeObserver = new ResizeObserver(() => Blockly.svgResize(workspace));
        resizeObserver.observe(containerRef.current);

        return () => {
            resizeObserver.disconnect();
            workspace.removeChangeListener(listener);
            workspace.dispose();
            workspaceRef.current = null;
        };
    }, []);

    useEffect(() => {
        const workspace = workspaceRef.current;
        if (!workspace) return;

        isLoadingRef.current = true;
        workspace.clear();
        if (objectType.program) {
            try {
                Blockly.serialization.workspaces.load(JSON.parse(objectType.program), workspace);
            } catch {
                // corrupt program, start fresh
            }
        }
        isLoadingRef.current = false;
    }, [objectType.id]);

    return <div className="script-editor">
        <div ref={containerRef} className="blockly-container" />
    </div>;
});
