import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

class ModelPreviewer {
    constructor(containerId) {
        this.container = document.getElementById(containerId);
        if (!this.container) return;

        this.hiddenInput = document.getElementById('_dprcu_camera_position');

        this.width = this.container.clientWidth;
        this.height = this.container.clientHeight;
        this.scene = new THREE.Scene();
        this.scene.background = new THREE.Color(0xf0f0f0);
        this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.1, 1000);
        this.camera.position.set(2, 2, 2);
        this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        this.renderer.setSize(this.width, this.height);
        this.renderer.outputColorSpace = THREE.SRGBColorSpace;

        const oldCanvas = this.container.querySelector('canvas');
        if (oldCanvas) oldCanvas.remove();

        this.container.appendChild(this.renderer.domElement);
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        this.controls.enableDamping = true;
        const ambient = new THREE.AmbientLight(0xffffff, 0.6);
        this.scene.add(ambient);
        const dirLight = new THREE.DirectionalLight(0xffffff, 1.2);
        dirLight.position.set(5, 10, 7);
        this.scene.add(dirLight);
        const backLight = new THREE.DirectionalLight(0xffffff, 0.5);
        backLight.position.set(-5, 5, -7);
        this.scene.add(backLight);
        const grid = new THREE.GridHelper(10, 10, 0x888888, 0xcccccc);
        this.scene.add(grid);
        this.resizeObserver = new ResizeObserver(() => {
            this.width = this.container.clientWidth;
            this.height = this.container.clientHeight;
            this.camera.aspect = this.width / this.height;
            this.camera.updateProjectionMatrix();
            this.renderer.setSize(this.width, this.height);
        });
        this.resizeObserver.observe(this.container);
        this.animate = this.animate.bind(this);
        this.animate();

        this._attachButtons();
    }
    _attachButtons() {
        const saveBtn = document.getElementById('cam-save-btn');
        const resetBtn = document.getElementById('cam-reset-btn');

        if (saveBtn) {
            saveBtn.onclick = () => {
                if (!this.currentModel) return;
                const state = JSON.stringify({
                    position: {
                        x: parseFloat(this.camera.position.x.toFixed(4)),
                        y: parseFloat(this.camera.position.y.toFixed(4)),
                        z: parseFloat(this.camera.position.z.toFixed(4)),
                    },
                    target: {
                        x: parseFloat(this.controls.target.x.toFixed(4)),
                        y: parseFloat(this.controls.target.y.toFixed(4)),
                        z: parseFloat(this.controls.target.z.toFixed(4)),
                    },
                });
                if (this.hiddenInput) this.hiddenInput.value = state;
                saveBtn.textContent = 'Saved!';
                setTimeout(() => { saveBtn.textContent = 'Save View'; }, 1500);
            };
        }

        if (resetBtn) {
            resetBtn.onclick = () => {
                if (this.hiddenInput) this.hiddenInput.value = '';
                resetBtn.textContent = 'Reset!';
                setTimeout(() => { resetBtn.textContent = 'Reset View'; }, 1500);
                if (!this.currentModel) return;
                const box = new THREE.Box3().setFromObject(this.currentModel);
                const size = new THREE.Vector3(); box.getSize(size);
                const maxDim = Math.max(size.x, size.y, size.z);
                const fov = this.camera.fov * (Math.PI / 180);
                const cameraDist = Math.abs(maxDim / 2 / Math.tan(fov / 2));
                this.camera.position.set(cameraDist, cameraDist * 0.6, cameraDist);
                this.camera.lookAt(0, 0, 0);
                this.controls.target.set(0, 0, 0);
                this.controls.update();
            };
        }
    }
    _loadSavedCameraState() {
        if (!this.hiddenInput || !this.hiddenInput.value) return null;
        try {
            const state = JSON.parse(this.hiddenInput.value);
            if (state?.position && state?.target) return state;
        } catch (e) { /* ignore invalid JSON */ }
        return null;
    }
    load(url) {
        this.container.style.display = 'block';
        if (this.currentModel) {
            this.scene.remove(this.currentModel);
            this.currentModel = null;
        }
        const loader = new GLTFLoader();
        loader.load(url, (gltf) => {
            this.currentModel = gltf.scene;
            this.scene.add(this.currentModel);
            const box = new THREE.Box3().setFromObject(this.currentModel);
            const size = new THREE.Vector3(); box.getSize(size);
            const center = new THREE.Vector3(); box.getCenter(center);
            this.currentModel.position.x += (this.currentModel.position.x - center.x);
            this.currentModel.position.y += (this.currentModel.position.y - center.y);
            this.currentModel.position.z += (this.currentModel.position.z - center.z);
            const maxDim = Math.max(size.x, size.y, size.z);
            const fov = this.camera.fov * (Math.PI / 180);
            const cameraDist = Math.abs(maxDim / 2 / Math.tan(fov / 2));

            const saved = this._loadSavedCameraState();
            if (saved) {
                this.camera.position.set(saved.position.x, saved.position.y, saved.position.z);
                this.controls.target.set(saved.target.x, saved.target.y, saved.target.z);
            } else {
                this.camera.position.set(cameraDist, cameraDist * 0.6, cameraDist);
                this.camera.lookAt(0, 0, 0);
                this.controls.target.set(0, 0, 0);
            }
            this.controls.update();
        }, undefined, (err) => console.error('Preview Error:', err));
    }
    animate() {
        requestAnimationFrame(this.animate);
        this.controls.update();
        this.renderer.render(this.scene, this.camera);
    }
}

jQuery(document).ready(($) => {

    const $input = $('#_3d_model_url_field');
    const $status = $('#parser-status');
    let previewer = null;
    if (document.getElementById('admin-model-preview')) {
        previewer = new ModelPreviewer('admin-model-preview');
    }

    function performScan(url) {
        const loader = new GLTFLoader();

        $status.show().html('Scanning model... <span class="spinner is-active" style="float:none; margin:0;"></span>');

        loader.load(url, (gltf) => {
            const meshes = [];
            gltf.scene.traverse((child) => {
                if (child.isMesh && child.name) meshes.push(child.name);
            });

            const uniqueMeshes = [...new Set(meshes)].sort();

            if (typeof window.updateDetectedPartsDisplay === 'function') {
                window.updateDetectedPartsDisplay(uniqueMeshes);
            }

            $status.html('<span style="color:#00a32a">✔ Found ' + uniqueMeshes.length + ' parts.</span>');
            setTimeout(() => $status.fadeOut(), 3000);

        }, undefined, (err) => {
            console.error(err);
            $status.html('<span style="color:#d63638">✘ Error loading model.</span>');
        });
    }

    const initialUrl = $input.val();
    if (initialUrl && (initialUrl.endsWith('.glb') || initialUrl.endsWith('.gltf'))) {
        if (previewer) previewer.load(initialUrl);
        performScan(initialUrl);
    }

    $input.on('change input', function () {
        const url = $(this).val();
        if (!url || (!url.endsWith('.glb') && !url.endsWith('.gltf'))) return;

        if (previewer) previewer.load(url);

        performScan(url);
    });
});
