import * as THREE from 'three';

export function useCityObjects() {
    const buildings: THREE.Mesh[] = [];
    const interactableObjects: THREE.Object3D[] = [];
    const sceneObjects: THREE.Object3D[] = [];

    // --- Assets & Materials ---
    const MATERIALS = {
        road: new THREE.MeshLambertMaterial({ color: 0x333333 }),
        roadLine: new THREE.MeshBasicMaterial({ color: 0xffffff }),
        sidewalk: new THREE.MeshLambertMaterial({ color: 0xcccccc }),
        grass: new THREE.MeshLambertMaterial({ color: 0x7cfc00 }),
        treeTrunk: new THREE.MeshLambertMaterial({ color: 0x8b4513 }),
        treeLeaves: new THREE.MeshLambertMaterial({ color: 0x228b22 }),
    };

    const buildingTypes: Record<string, any> = {
        house: {
            geometry: new THREE.BoxGeometry(6, 6, 6),
            material: new THREE.MeshLambertMaterial({ color: 0xffaa00 }), // Vibrant Orange
            description: "Residential House"
        },
        factory: {
            geometry: new THREE.BoxGeometry(8, 6, 10),
            material: new THREE.MeshLambertMaterial({ color: 0xe74c3c }), // Red
            description: "Manufacturing Plant"
        },
        office: {
            geometry: new THREE.BoxGeometry(8, 14, 8),
            material: new THREE.MeshLambertMaterial({ color: 0x3498db }), // Blue
            description: "Office Building"
        },
        shop: {
            geometry: new THREE.BoxGeometry(8, 5, 8),
            material: new THREE.MeshLambertMaterial({ color: 0xf1c40f }), // Yellow
            description: "Retail Shop"
        }
    };

    // --- City Generation Config ---
    const BLOCK_SIZE = 30; // Size of one grid cell (including road half-width)
    const ROAD_WIDTH = 8;
    const SIDEWALK_WIDTH = 1.5;
    const GRID_SIZE = 6; // 6x6 grid

    // --- Instanced Mesh Config ---
    const MAX_TREES = 200; // Safe upper bound for 6x6 grid with max 3 trees per plot
    const GRID_AREA = GRID_SIZE * GRID_SIZE; // 36

    const createGenericBuilding = (scene: THREE.Scene, buildingData: any, position: THREE.Vector3, id: string, csvPath: string, rotation: number = 0) => {
        const mesh = new THREE.Mesh(buildingData.geometry, buildingData.material);
        mesh.position.copy(position);
        mesh.rotation.y = rotation;

        if (buildingData.geometry.type === 'BoxGeometry') {
            mesh.position.y += buildingData.geometry.parameters.height / 2;
        }
        mesh.castShadow = true;
        mesh.receiveShadow = true;
        mesh.userData = {
            id,
            type: buildingData.description || 'Generic',
            ...buildingData,
            csvPath
        };
        interactableObjects.push(mesh);
        buildings.push(mesh);
        sceneObjects.push(mesh); // Track
        scene.add(mesh);
    };

    const createBuilding = (scene: THREE.Scene, type: string, position: THREE.Vector3, id: string, csvPath: string, rotation: number = 0) => {
        const buildingData = buildingTypes[type];
        if (!buildingData) {
            console.warn(`Building type ${type} not found`);
            return;
        }
        // Adjust position to sit on sidewalk/grass (y=0.5)
        const pos = position.clone();
        pos.y = 0.5;

        createGenericBuilding(scene, buildingData, pos, id, csvPath, rotation);
    };

    const initGround = (scene: THREE.Scene) => {
        // ... (Base creation)
        const baseGeo = new THREE.PlaneGeometry(300, 300);
        const base = new THREE.Mesh(baseGeo, MATERIALS.road);
        // ...
        base.rotation.x = -Math.PI / 2;
        base.position.y = -0.1;
        base.receiveShadow = true;
        scene.add(base);
        sceneObjects.push(base); // Track

        const plots: THREE.Vector3[] = [];
        const offset = ((GRID_SIZE - 1) * BLOCK_SIZE) / 2;

        // ... (Instanced Meshes)
        const sidewalkGeo = new THREE.BoxGeometry(BLOCK_SIZE - ROAD_WIDTH, 0.4, BLOCK_SIZE - ROAD_WIDTH);
        const sidewalkMesh = new THREE.InstancedMesh(sidewalkGeo, MATERIALS.sidewalk, GRID_AREA);
        sidewalkMesh.receiveShadow = true;
        sceneObjects.push(sidewalkMesh); // Track

        const grassSize = (BLOCK_SIZE - ROAD_WIDTH) - (SIDEWALK_WIDTH * 2);
        const grassGeo = new THREE.BoxGeometry(grassSize, 0.5, grassSize);
        const grassMesh = new THREE.InstancedMesh(grassGeo, MATERIALS.grass, GRID_AREA);
        grassMesh.receiveShadow = true;
        sceneObjects.push(grassMesh); // Track

        // ... (Loop to fill instances and plots - remains same)
        const dummy = new THREE.Object3D();
        let instanceIdx = 0;

        for (let ix = 0; ix < GRID_SIZE; ix++) {
            for (let iz = 0; iz < GRID_SIZE; iz++) {
                const x = (ix * BLOCK_SIZE) - offset;
                const z = (iz * BLOCK_SIZE) - offset;

                // Position Helper
                dummy.position.set(x, 0.2, z);
                dummy.rotation.set(0, 0, 0);
                dummy.scale.set(1, 1, 1);
                dummy.updateMatrix();
                sidewalkMesh.setMatrixAt(instanceIdx, dummy.matrix);

                dummy.position.set(x, 0.25, z);
                dummy.updateMatrix();
                grassMesh.setMatrixAt(instanceIdx, dummy.matrix);

                plots.push(new THREE.Vector3(x, 0, z));
                instanceIdx++;
            }
        }

        scene.add(sidewalkMesh);
        scene.add(grassMesh);

        return plots;
    };

    const initBuildings = (scene: THREE.Scene, plots: THREE.Vector3[], buildingDefs: any[]) => {
        // ... (Trees setup)
        const trunkGeo = new THREE.CylinderGeometry(0.5, 0.5, 2, 6);
        const leavesGeo = new THREE.ConeGeometry(2, 4, 8);

        const trunkMesh = new THREE.InstancedMesh(trunkGeo, MATERIALS.treeTrunk, MAX_TREES);
        const leavesMesh = new THREE.InstancedMesh(leavesGeo, MATERIALS.treeLeaves, MAX_TREES);
        trunkMesh.castShadow = true; trunkMesh.receiveShadow = true;
        leavesMesh.castShadow = true; leavesMesh.receiveShadow = true;
        sceneObjects.push(trunkMesh); // Track
        sceneObjects.push(leavesMesh); // Track

        let treeCount = 0;
        const dummy = new THREE.Object3D();

        const addTreeInstance = (pos: THREE.Vector3) => {
            // ... (remains same)
            if (treeCount >= MAX_TREES) return;
            dummy.position.copy(pos).add(new THREE.Vector3(0, 1, 0));
            dummy.rotation.set(0, 0, 0);
            dummy.scale.set(1, 1, 1);
            dummy.updateMatrix();
            trunkMesh.setMatrixAt(treeCount, dummy.matrix);

            dummy.position.copy(pos).add(new THREE.Vector3(0, 4, 0));
            dummy.updateMatrix();
            leavesMesh.setMatrixAt(treeCount, dummy.matrix);

            treeCount++;
        };

        let buildingIndex = 0;

        for (let i = 0; i < plots.length; i++) {
            const plot = plots[i];

            if (buildingIndex < buildingDefs.length) {
                const def = buildingDefs[buildingIndex];
                // Support both 'csv' and 'csvPath' keys for compatibility if needed, but new state uses csvPath
                createBuilding(scene, def.type, plot, def.id, def.csvPath || def.csv, 0);
                buildingIndex++;
            } else {
                // Empty plot filler: Trees
                addTreeInstance(plot.clone().add(new THREE.Vector3(-5, 0.5, -5)));
                if (Math.random() > 0.5) {
                    addTreeInstance(plot.clone().add(new THREE.Vector3(6, 0.5, 4)));
                }
            }
        }

        trunkMesh.count = treeCount;
        leavesMesh.count = treeCount;
        scene.add(trunkMesh);
        scene.add(leavesMesh);
    };

    const clearCity = (scene: THREE.Scene) => {
        sceneObjects.forEach(obj => {
            scene.remove(obj);
            // Optional: Dispose geometries/materials if strictly needed
        });
        sceneObjects.length = 0;
        buildings.length = 0;
        interactableObjects.length = 0;
    };

    return {
        interactableObjects,
        initCity: (scene: THREE.Scene, buildingDefs: any[]) => {
            const plots = initGround(scene);
            initBuildings(scene, plots, buildingDefs);
        },
        clearCity
    };
}
