import React, { useEffect, useMemo } from "react";
import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from "three";
import { Html, Extrude } from "@react-three/drei";
import { useLoader } from "@react-three/fiber";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { useSceneContext } from "../../../scene/sceneContext";
import { useToggleView, useToolMode } from "../../../../store/builder/store";
import { useWallClassification } from "./instance/helpers/useWallClassification";
import Line from "../../line/line";
import Point from "../../point/point";
import WallInstance from "./instance/wallInstance";
import * as Constants from "../../../../types/world/worldConstants";

import texturePath from "../../../../assets/textures/floor/white.png";
import texturePathDark from "../../../../assets/textures/floor/black.png";
import useModuleStore from "../../../../store/ui/useModuleStore";
import getCenteroidPoint from "../../functions/getCenteroid";
import getArea from "../../functions/getArea";

function WallInstances() {
    const { wallStore } = useSceneContext();
    const { setSelectedWall, selectedWall } = useBuilderStore();
    const { toolMode } = useToolMode();
    const { toggleView } = useToggleView();
    const { activeModule } = useModuleStore();
    const { walls } = wallStore();
    const { rooms } = useWallClassification(walls);

    useEffect(() => {
        // console.log('walls: ', walls);
    }, [walls]);

    useEffect(() => {
        if (!toggleView && activeModule === "builder") {
            if (toolMode !== "cursor") {
                if (selectedWall) setSelectedWall(null);
            }
        } else {
            if (selectedWall) setSelectedWall(null);
        }
    }, [toggleView, toolMode, activeModule, selectedWall]);

    const allPoints = useMemo(() => {
        const points: Point[] = [];
        const seenUuids = new Set<string>();

        walls.forEach((wall) => {
            wall.points.forEach((point) => {
                if (!seenUuids.has(point.pointUuid)) {
                    seenUuids.add(point.pointUuid);
                    points.push(point);
                }
            });
        });

        return points;
    }, [walls]);

    return (
        <>
            {!toggleView && (
                <>
                    {walls.map((wall) => (
                        <WallInstance key={wall.wallUuid} wall={wall} />
                    ))}

                    <group name="Wall-Floors-Group">
                        {rooms.map((room, index) => (
                            <Floor3D key={index} room={room} />
                        ))}
                    </group>
                </>
            )}

            {toggleView && (
                <>
                    <group name="Wall-Points-Group">
                        {allPoints.map((point) => (
                            <Point key={point.pointUuid} point={point} />
                        ))}
                    </group>

                    <group name="Wall-Floors-Group">
                        {rooms.map((room, index) => (
                            <Floor2D key={index} room={room} />
                        ))}
                    </group>

                    <group name="Wall-Lines-Group">
                        {walls.map((wall) => (
                            <React.Fragment key={wall.wallUuid}>
                                <Line points={wall.points} />
                            </React.Fragment>
                        ))}

                        {walls.map((wall) => {
                            const textPosition = new Vector3().addVectors(new Vector3(...wall.points[0].position), new Vector3(...wall.points[1].position)).divideScalar(2);
                            const distance = new Vector3(...wall.points[0].position).distanceTo(new Vector3(...wall.points[1].position));

                            return (
                                <React.Fragment key={wall.wallUuid}>
                                    {toggleView && (
                                        <Html
                                            key={`${wall.points[0].pointUuid}_${wall.points[1].pointUuid}`}
                                            userData={wall}
                                            position={[textPosition.x, 1, textPosition.z]}
                                            wrapperClass="distance-text-wrapper"
                                            className="distance-text"
                                            zIndexRange={[1, 0]}
                                            prepend
                                            sprite
                                        >
                                            <div key={wall.wallUuid} className={`distance ${wall.wallUuid}`}>
                                                {distance.toFixed(2)} m
                                            </div>
                                        </Html>
                                    )}
                                </React.Fragment>
                            );
                        })}
                    </group>
                </>
            )}
        </>
    );
}

export default WallInstances;

function Floor3D({ room }: { readonly room: Point[] }) {
    const savedTheme: string | null = localStorage.getItem("theme");
    const textureScale = Constants.floorConfig.textureScale;
    const floorTexture = useLoader(TextureLoader, savedTheme === "dark" ? texturePathDark : texturePath);
    floorTexture.wrapS = floorTexture.wrapT = RepeatWrapping;
    floorTexture.repeat.set(textureScale, textureScale);
    floorTexture.colorSpace = SRGBColorSpace;

    const shape = useMemo(() => {
        const shape = new Shape();
        const points = room.map((p) => new Vector2(p.position[0], p.position[2]));
        if (points.length < 3) return null;
        shape.moveTo(points[0].x, points[0].y);
        points.forEach((pt) => {
            shape.lineTo(pt.x, pt.y);
        });
        return shape;
    }, [room]);

    if (!shape) return null;

    return (
        <mesh name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}>
            <Extrude receiveShadow castShadow name="Wall-Floor" args={[shape, { depth: Constants.floorConfig.height, bevelEnabled: false }]} position={[0, 0, 0]}>
                <meshStandardMaterial color={Constants.floorConfig.defaultColor} map={floorTexture} side={DoubleSide} />
            </Extrude>
        </mesh>
    );
}

function Floor2D({ room }: { readonly room: Point[] }) {
    const savedTheme: string | null = localStorage.getItem("theme");

    const points2D = useMemo(() => {
        return room.map((p) => new Vector2(parseFloat(p.position[0].toFixed(2)), parseFloat(p.position[2].toFixed(2))));
    }, [room]);

    const shape = useMemo(() => {
        const shape = new Shape();
        shape.moveTo(points2D[0].x, points2D[0].y);
        for (let i = 1; i < points2D.length; i++) {
            shape.lineTo(points2D[i].x, points2D[i].y);
        }
        shape.lineTo(points2D[0].x, points2D[0].y);
        return shape;
    }, [points2D]);

    const area = useMemo(() => getArea(points2D), [points2D]);

    const centroid: [number, number, number] = useMemo(() => {
        const center = getCenteroidPoint(points2D);
        if (!center) return [0, Constants.floorConfig.height + 0.01, 0];

        return [center.x, Constants.floorConfig.height + 0.01, center.y] as [number, number, number];
    }, [points2D]);

    if (!shape) return null;

    const formattedArea = `${area.toFixed(2)} m²`;

    return (
        <>
            <mesh castShadow receiveShadow name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}>
                <Extrude receiveShadow castShadow name="Wall-Floor" args={[shape, { depth: Constants.floorConfig.height }]} position={[0, 0, 0]}>
                    <meshBasicMaterial color={savedTheme === "dark" ? Constants.lineConfig.wallColor : Constants.lineConfig.wallColor} side={DoubleSide} transparent opacity={0.4} depthWrite={false} />
                </Extrude>
            </mesh>

            <Html position={centroid} wrapperClass="distance-text-wrapper" className="distance-text" zIndexRange={[1, 0]} prepend center sprite>
                <div className="distance area">({formattedArea})</div>
            </Html>
        </>
    );
}
