import { observer } from "mobx-react-lite";
import type { ObjectInstance, R3, Runtime } from "../types";
import { useMemo, useRef, type RefObject } from "react";
import type { Group } from "three";
import { TransformControls, useHelper } from "@react-three/drei";
import { BoxHelper } from "three";
import type { ThreeEvent } from "@react-three/fiber";
import { state } from "../state";
import { nextSelectionEditMode } from "../state/worldEditorState";
import { ObjectViewInternal, type ObjectViewInternalHandle } from "./ObjectViewInternal";

type ObjectEditorViewProps = {
    object: Runtime<ObjectInstance>;
}

type SelectionOverlayProps = {
    objectId: string;
    ref: RefObject<ObjectViewInternalHandle | null>;
    onTransformEnd: () => void;
}

// Separate observer so only the 2 affected instances (selected/deselected)
// re-render on selection change, not all N objects in the scene.
const SelectionOverlay = observer(function ({ objectId, ref, onTransformEnd }: SelectionOverlayProps) {
    const isSelected = state.worldEditor.isEnabled &&
        state.worldEditor.selectedObjectId === objectId;
    const selectionMode = isSelected ? state.worldEditor.selectedObjectMode : undefined;

    // Stable virtual ref that reads through to the group inside the handle
    const groupRef = useMemo<RefObject<Group | null>>(
        () => ({ get current() { return ref.current?.group ?? null; } }),
        [ref],
    );

    useHelper(isSelected ? groupRef : { current: null } as any, BoxHelper, 'white');

    if (!isSelected || selectionMode === undefined || !groupRef.current)
        return null;

    return (
        <TransformControls
            object={groupRef as RefObject<Group>}
            mode={selectionMode}
            onMouseUp={onTransformEnd}
        />
    );
});

export const ObjectEditorView = observer(function ({ object }: ObjectEditorViewProps) {
    const dataRef = useRef<ObjectViewInternalHandle>(null);

    // Only observes world object types — not selection state — so won't
    // re-render when a different object is selected.
    const objectType = state.world.getObjectTypeById(object.typeId);
    if (!objectType)
        return null;

    function handleClick(e: ThreeEvent<MouseEvent>) {
        if (e.delta > 5) return;
        e.stopPropagation();
        // Reading selection state inside an event handler: not tracked by observer.
        const currentMode = state.worldEditor.isEnabled &&
            state.worldEditor.selectedObjectId === object.id
            ? state.worldEditor.selectedObjectMode
            : undefined;
        state.worldEditor.setSelectedObject(object.id, nextSelectionEditMode(currentMode));
    }

    function handleTransformEnd() {
        const group = dataRef.current?.group;
        if (group)
            state.worldEditor.setObjectTransform(
                object.id,
                group.position.toArray(),
                group.rotation.toArray().slice(0, 3) as R3,
                group.scale.toArray(),
            );
    }

    return (<>
        <SelectionOverlay
            objectId={object.id}
            ref={dataRef}
            onTransformEnd={handleTransformEnd}
        />
        <ObjectViewInternal
            ref={dataRef}
            object={object}
            isEditor
            objectType={objectType}
            onClick={handleClick}
        />
    </>);
});
