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

export function useWallClassification(walls: Walls) {
    // Find all minimal rooms from the given walls
    const findRooms = () => {
        if (walls.length < 3) return [];

        // Build a graph of point connections
        const graph = new Map<string, string[]>();
        const pointMap = new Map<string, Point>();

        walls.forEach(wall => {
            const [p1, p2] = wall.points;
            pointMap.set(p1.pointUuid, p1);
            pointMap.set(p2.pointUuid, p2);

            // Add connection from p1 to p2
            if (!graph.has(p1.pointUuid)) graph.set(p1.pointUuid, []);
            graph.get(p1.pointUuid)?.push(p2.pointUuid);

            // Add connection from p2 to p1
            if (!graph.has(p2.pointUuid)) graph.set(p2.pointUuid, []);
            graph.get(p2.pointUuid)?.push(p1.pointUuid);
        });

        // Find all minimal cycles (rooms) in the graph
        const allCycles: string[][] = [];

        const findCycles = (startNode: string, currentNode: string, path: string[], depth = 0) => {
            if (depth > 20) return; // Prevent infinite recursion

            path.push(currentNode);

            const neighbors = graph.get(currentNode) || [];

            for (const neighbor of neighbors) {
                if (path.length > 2 && neighbor === startNode) {
                    // Found a cycle that returns to start
                    const cycle = [...path, neighbor];

                    // Check if this is a new unique cycle
                    if (!cycleExists(allCycles, cycle)) {
                        allCycles.push(cycle);
                    }
                    continue;
                }

                if (!path.includes(neighbor)) {
                    findCycles(startNode, neighbor, [...path], depth + 1);
                }
            }
        };

        // Start cycle detection from each node
        for (const [pointUuid] of graph) {
            findCycles(pointUuid, pointUuid, []);
        }

        // Convert cycles to Point arrays and validate them
        const potentialRooms = allCycles
            .map(cycle => cycle.map(uuid => pointMap.get(uuid)!))
            .filter(room => isValidRoom(room));

        const uniqueRooms = removeDuplicateRooms(potentialRooms);

        // ✅ New logic that only removes redundant supersets
        const filteredRooms = removeSupersetLikeRooms(uniqueRooms, walls);

        return filteredRooms;

    };

    const removeSupersetLikeRooms = (rooms: Point[][], walls: Wall[]): Point[][] => {
        const toRemove = new Set<number>();

        const getPolygon = (points: Point[]) =>
            turf.polygon([points.map(p => [p.position[0], p.position[2]])]);

        const getWallSet = (room: Point[]) => {
            const set = new Set<string>();
            for (let i = 0; i < room.length - 1; i++) {
                const p1 = room[i].pointUuid;
                const p2 = room[i + 1].pointUuid;

                const wall = walls.find(w =>
                    (w.points[0].pointUuid === p1 && w.points[1].pointUuid === p2) ||
                    (w.points[0].pointUuid === p2 && w.points[1].pointUuid === p1)
                );

                if (wall) {
                    set.add(wall.wallUuid);
                }
            }
            return set;
        };

        const roomPolygons = rooms.map(getPolygon);
        const roomAreas = roomPolygons.map(poly => turf.area(poly));
        const wallSets = rooms.map(getWallSet);

        // First, identify all rooms that are completely contained within others
        for (let i = 0; i < rooms.length; i++) {
            for (let j = 0; j < rooms.length; j++) {
                if (i === j) continue;

                // If room i completely contains room j
                if (turf.booleanContains(roomPolygons[i], roomPolygons[j])) {
                    // Check if the contained room shares most of its walls with the containing room
                    const sharedWalls = [...wallSets[j]].filter(w => wallSets[i].has(w));
                    const shareRatio = sharedWalls.length / wallSets[j].size;

                    // If they share more than 50% of walls, mark the larger one for removal
                    // UNLESS the smaller one is significantly smaller (likely a real room)
                    if (shareRatio > 0.5 && (roomAreas[i] / roomAreas[j] > 2)) {
                        toRemove.add(i);
                    }
                }
            }
        }

        // Second pass: handle cases where a room is divided by segmented walls
        for (let i = 0; i < rooms.length; i++) {
            if (toRemove.has(i)) continue;

            for (let j = 0; j < rooms.length; j++) {
                if (i === j || toRemove.has(j)) continue;

                // Check if these rooms share a significant portion of walls
                const sharedWalls = [...wallSets[i]].filter(w => wallSets[j].has(w));
                const shareRatio = Math.max(
                    sharedWalls.length / wallSets[i].size,
                    sharedWalls.length / wallSets[j].size
                );

                // If they share more than 30% of walls and one is much larger
                if (shareRatio > 0.3) {
                    const areaRatio = roomAreas[i] / roomAreas[j];
                    if (areaRatio > 2) {
                        // The larger room is likely the undivided version
                        toRemove.add(i);
                    } else if (areaRatio < 0.5) {
                        // The smaller room might be an artifact
                        toRemove.add(j);
                    }
                }
            }
        }

        return rooms.filter((_, idx) => !toRemove.has(idx));
    };

    // Check if a cycle already exists in our list (considering different orders)
    const cycleExists = (allCycles: string[][], newCycle: string[]) => {
        const newSet = new Set(newCycle);

        return allCycles.some(existingCycle => {
            if (existingCycle.length !== newCycle.length) return false;
            const existingSet = new Set(existingCycle);
            return setsEqual(newSet, existingSet);
        });
    };

    // Check if two sets are equal
    const setsEqual = <T,>(a: Set<T>, b: Set<T>) => {
        if (a.size !== b.size) return false;
        for (const item of a) if (!b.has(item)) return false;
        return true;
    };

    // Remove duplicate rooms (same set of points in different orders)
    const removeDuplicateRooms = (rooms: Point[][]) => {
        const uniqueRooms: Point[][] = [];
        const roomHashes = new Set<string>();

        for (const room of rooms) {
            // Create a consistent hash for the room regardless of point order
            const hash = room
                .map(p => p.pointUuid)
                .sort()
                .join('-');

            if (!roomHashes.has(hash)) {
                roomHashes.add(hash);
                uniqueRooms.push(room);
            }
        }

        return uniqueRooms;
    };

    // Check if a room is valid (closed, non-self-intersecting polygon)
    const isValidRoom = (points: Point[]): boolean => {
        // Must have at least 4 points (first and last are same)
        if (points.length < 4) return false;

        // Must be a closed loop
        if (points[0].pointUuid !== points[points.length - 1].pointUuid) {
            return false;
        }

        try {
            const coordinates = points.map(p => [p.position[0], p.position[2]]);
            const polygon = turf.polygon([coordinates]);
            return turf.booleanValid(polygon);
        } catch (e) {
            return false;
        }
    };

    // Rest of the implementation remains the same...
    const rooms = useMemo(() => findRooms(), [walls]);

    // Determine wall orientation relative to room
    const findWallType = (wall: Wall) => {
        // Find all rooms that contain this 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
            };
        }
    };

    // Update the other functions to use the new return type
    const getWallType = (wall: Wall): {
        type: string;
        rooms: Point[][];
    } => {
        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';
    };

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