import { useMemo } from 'react';
import * as turf from '@turf/turf';

export function useWallClassification(walls: Walls) {

    const findRooms = () => {
        if (walls.length < 3) return [];

        // Map pointUuid to list of connected line segments
        const pointMap = new Map<string, Wall[]>();

        for (const wall of walls) {
            for (const point of wall.points) {
                const list = pointMap.get(point.pointUuid) || [];
                list.push(wall);
                pointMap.set(point.pointUuid, list);
            }
        }

        // Create graph of connected walls using pointUuid
        const visited = new Set<string>();
        const mergedLineStrings = [];

        const wallKey = (p1: Point, p2: Point) => `${p1.pointUuid}-${p2.pointUuid}`;

        for (const wall of walls) {
            const key = wallKey(wall.points[0], wall.points[1]);
            if (visited.has(key) || visited.has(wallKey(wall.points[1], wall.points[0]))) continue;

            let line: Point[] = [...wall.points];
            visited.add(key);

            // Try to extend the line forward and backward by matching endpoints
            let extended = true;
            while (extended) {
                extended = false;

                const last = line[line.length - 1];
                const nextWalls = pointMap.get(last.pointUuid) || [];

                for (const next of nextWalls) {
                    const [n1, n2] = next.points;
                    const nextKey = wallKey(n1, n2);
                    if (visited.has(nextKey) || visited.has(wallKey(n2, n1))) continue;

                    if (n1.pointUuid === last.pointUuid && n2.pointUuid !== line[line.length - 2]?.pointUuid) {
                        line.push(n2);
                        visited.add(nextKey);
                        extended = true;
                        break;
                    } else if (n2.pointUuid === last.pointUuid && n1.pointUuid !== line[line.length - 2]?.pointUuid) {
                        line.push(n1);
                        visited.add(nextKey);
                        extended = true;
                        break;
                    }
                }

                const first = line[0];
                const prevWalls = pointMap.get(first.pointUuid) || [];

                for (const prev of prevWalls) {
                    const [p1, p2] = prev.points;
                    const prevKey = wallKey(p1, p2);
                    if (visited.has(prevKey) || visited.has(wallKey(p2, p1))) continue;

                    if (p1.pointUuid === first.pointUuid && p2.pointUuid !== line[1]?.pointUuid) {
                        line.unshift(p2);
                        visited.add(prevKey);
                        extended = true;
                        break;
                    } else if (p2.pointUuid === first.pointUuid && p1.pointUuid !== line[1]?.pointUuid) {
                        line.unshift(p1);
                        visited.add(prevKey);
                        extended = true;
                        break;
                    }
                }
            }

            // Create merged LineString
            const coords = line.map(p => [p.position[0], p.position[2]]);
            mergedLineStrings.push(turf.lineString(coords, {
                pointUuids: line.map(p => p.pointUuid)
            }));
        }

        const validLineStrings = mergedLineStrings.map(ls => {
            const coords = ls.geometry.coordinates.map(coord => coord.join(','));

            if (coords.length < 2) return null;

            const start = coords[0];
            const end = coords[coords.length - 1];
            const middle = coords.slice(1, -1);

            const seen = new Set<string>([start, end]);
            const filteredMiddle: string[] = [];

            for (const point of middle) {
                if (!seen.has(point)) {
                    seen.add(point);
                    filteredMiddle.push(point);
                }
            }

            const newCoords = [start, ...filteredMiddle, end];

            if (newCoords.length >= 4) {
                const resultCoords = newCoords.map(str => str.split(',').map(Number));
                return {
                    ...ls,
                    geometry: {
                        ...ls.geometry,
                        coordinates: resultCoords,
                    },
                };
            }

            return null;
        }).filter(Boolean);

        if (validLineStrings.length === 0) return [];

        const lineStrings = turf.featureCollection(validLineStrings as any);
        const polygons = turf.polygonize(lineStrings as any);

        const rooms: Point[][] = [];

        polygons.features.forEach(feature => {
            if (feature.geometry.type === 'Polygon') {
                const coordinates = feature.geometry.coordinates[0];
                const roomPoints: Point[] = [];

                for (const [x, z] of coordinates) {
                    const matchingPoint = walls.flatMap(wall => wall.points)
                        .find(p =>
                            p.position[0].toFixed(10) === x.toFixed(10) &&
                            p.position[2].toFixed(10) === z.toFixed(10)
                        );

                    if (matchingPoint) {
                        roomPoints.push(matchingPoint);
                    }
                }

                if (roomPoints.length > 0 &&
                    roomPoints[0].pointUuid !== roomPoints[roomPoints.length - 1].pointUuid) {
                    roomPoints.push(roomPoints[0]);
                }

                if (roomPoints.length >= 4) {
                    rooms.push(roomPoints);
                }
            }
        });

        return rooms;
    };

    const rooms = useMemo(() => findRooms(), [walls]);

    const findWallType = (wall: Wall) => {
        const containingRooms = rooms.filter(room => {
            for (let i = 0; i < room.length - 1; i++) {
                const p1 = room[i];
                const p2 = room[i + 1];
                if ((wall.points[0].pointUuid === p1.pointUuid && wall.points[1].pointUuid === p2.pointUuid) ||
                    (wall.points[0].pointUuid === p2.pointUuid && wall.points[1].pointUuid === p1.pointUuid)) {
                    return true;
                }
            }
            return false;
        });

        if (containingRooms.length === 0) {
            return {
                type: 'segment',
                rooms: []
            };
        } else if (containingRooms.length === 1) {
            return {
                type: 'room',
                rooms: containingRooms
            };
        } else {
            return {
                type: 'rooms',
                rooms: containingRooms
            };
        }
    };

    const getWallType = (wall: Wall) => {
        return findWallType(wall);
    };

    const isRoomWall = (wall: Wall): boolean => {
        const type = findWallType(wall).type;
        return type === 'room' || type === 'rooms';
    };

    const isSegmentWall = (wall: Wall): boolean => {
        return findWallType(wall).type === 'segment';
    };

    const isWallFlipped = (wall: Wall): boolean => {
        const wallType = findWallType(wall);
        if (wallType.type === 'segment') return false;

        for (const room of wallType.rooms) {
            for (let i = 0; i < room.length - 1; i++) {
                const p1 = room[i];
                const p2 = room[i + 1];
                if (wall.points[0].pointUuid === p1.pointUuid && wall.points[1].pointUuid === p2.pointUuid) {
                    return false;
                }
                if (wall.points[0].pointUuid === p2.pointUuid && wall.points[1].pointUuid === p1.pointUuid) {
                    return true;
                }
            }
        }
        return false;
    };


    return {
        rooms,
        getWallType,
        isRoomWall,
        isSegmentWall,
        findRooms,
        isWallFlipped
    };
}