import { OrbitControls } from '@react-three/drei';
import { useState } from 'react';
import type { ThreeEvent } from '@react-three/fiber';
import type { Voxel, V3 } from '../../types';

type Props = {
    voxels: Voxel[];
    mode: 'add' | 'remove';
    color: string;
    typeId: string;
    onAdd: (voxel: Voxel) => void;
    onRemove: (position: V3) => void;
};

const FLOOR_Y = 0;

function adjPosition(pos: V3, nx: number, ny: number, nz: number): V3 {
    return [pos[0] + Math.round(nx), pos[1] + Math.round(ny), pos[2] + Math.round(nz)];
}

function posEq(a: V3, b: V3) {
    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
}

const DRAG_THRESHOLD = 5;

export function VoxelEditorScene({ voxels, mode, color, typeId, onAdd, onRemove }: Props) {
    const [ghost, setGhost] = useState<V3 | null>(null);

    function handleVoxelClick(e: ThreeEvent<MouseEvent>, voxel: Voxel) {
        if (e.delta > DRAG_THRESHOLD) return;
        e.stopPropagation();
        if (mode === 'remove') {
            onRemove(voxel.position);
            setGhost(null);
        } else {
            if (!e.face) return;
            const { x, y, z } = e.face.normal;
            const pos = adjPosition(voxel.position, x, y, z);
            onAdd({ typeId, position: pos, color });
        }
    }

    function handleVoxelMove(e: ThreeEvent<PointerEvent>, voxel: Voxel) {
        e.stopPropagation();
        if (mode === 'remove') {
            setGhost(voxel.position);
        } else {
            setGhost(null);
        }
    }

    function handleFloorClick(e: ThreeEvent<MouseEvent>) {
        if (e.delta > DRAG_THRESHOLD || mode !== 'add') return;
        e.stopPropagation();
        const pos: V3 = [Math.floor(e.point.x), 0, Math.floor(e.point.z)];
        onAdd({ typeId, position: pos, color });
    }

    function handleFloorMove(e: ThreeEvent<PointerEvent>) {
        if (mode !== 'add') return;
        setGhost([Math.floor(e.point.x), 0, Math.floor(e.point.z)]);
    }

    const ghostOccupied = ghost && voxels.some(v => posEq(v.position, ghost));

    return (
        <>
            <ambientLight intensity={0.7} />
            <directionalLight position={[8, 12, 6]} intensity={1} castShadow />

            <OrbitControls makeDefault />

            {/* Floor plane */}
            <mesh
                rotation={[-Math.PI / 2, 0, 0]}
                position={[0, FLOOR_Y, 0]}
                onClick={handleFloorClick}
                onPointerMove={handleFloorMove}
                onPointerLeave={() => setGhost(null)}
            >
                <planeGeometry args={[40, 40]} />
                <meshStandardMaterial color="#2a2a3a" />
            </mesh>

            <gridHelper args={[40, 40, '#555', '#333']} position={[0, FLOOR_Y, 0]} />

            {/* Voxels */}
            {voxels.map((v, i) => {
                const isHovered = !!ghost && mode === 'remove' && posEq(v.position, ghost);
                return (
                    <mesh
                        key={i}
                        position={[v.position[0] + 0.5, v.position[1] + 0.5, v.position[2] + 0.5]}
                        onClick={(e) => handleVoxelClick(e, v)}
                        onPointerMove={(e) => handleVoxelMove(e, v)}
                        onPointerLeave={() => setGhost(null)}
                    >
                        <boxGeometry />
                        <meshStandardMaterial
                            color={v.color ?? '#888888'}
                            opacity={isHovered ? 0.4 : 1}
                            transparent={isHovered}
                        />
                    </mesh>
                );
            })}

            {/* Ghost voxel preview */}
            {ghost && mode === 'add' && !ghostOccupied && (
                <mesh
                    position={[ghost[0] + 0.5, ghost[1] + 0.5, ghost[2] + 0.5]}
                    raycast={() => null}
                >
                    <boxGeometry />
                    <meshStandardMaterial color={color} opacity={0.45} transparent />
                </mesh>
            )}
        </>
    );
}
