import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';

interface WallStore {
    walls: Wall[];
    setWalls: (walls: Wall[]) => void;
    addWall: (wall: Wall) => void;
    updateWall: (uuid: string, updated: Partial<Wall>) => Wall | undefined;
    removeWall: (uuid: string) => void;
    clearWalls: () => void;
    removeWallByPoints: (Points: [Point, Point]) => Wall | undefined;
    addDecal: (wallUuid: string, decal: Decal) => void;
    updateDecal: (decalUuid: string, decal: Decal) => void;
    removeDecal: (decalUuid: string) => void;
    updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void;
    updateDecalRotation: (decalUuid: string, rotation: number) => void;
    updateDecalScale: (decalUuid: string, scale: number) => void;

    removePoint: (pointUuid: string) => Wall[];
    setPosition: (pointUuid: string, position: [number, number, number]) => Wall[] | [];
    setLayer: (pointUuid: string, layer: number) => void;

    getWallById: (uuid: string) => Wall | undefined;
    getWallsByPointId: (uuid: string) => Wall[] | [];
    getWallByPoints: (points: Point[]) => Wall | undefined;
    getWallPointById: (uuid: string) => Point | undefined;
    getConnectedPoints: (uuid: string) => Point[];
}

export const createWallStore = () => {
    return create<WallStore>()(
        immer((set, get) => ({
            walls: [],

            setWalls: (walls) => set((state) => {
                state.walls = walls;
            }),

            addWall: (wall) => set((state) => {
                state.walls.push(wall);
            }),

            updateWall: (uuid, updated) => {
                let updatedWall: Wall | undefined;
                set((state) => {
                    const wall = state.walls.find(w => w.wallUuid === uuid);
                    if (wall) {
                        Object.assign(wall, updated);
                        updatedWall = JSON.parse(JSON.stringify(wall));
                    }
                });
                return updatedWall;
            },

            removeWall: (uuid) => set((state) => {
                state.walls = state.walls.filter(w => w.wallUuid !== uuid);
            }),

            clearWalls: () => {
                set((state) => {
                    state.walls = [];
                })
            },

            removeWallByPoints: (points) => {
                let removedWall: Wall | undefined;
                const [pointA, pointB] = points;

                set((state) => {
                    state.walls = state.walls.filter(wall => {
                        const wallPoints = wall.points.map(p => p.pointUuid);
                        const hasBothPoints = wallPoints.includes(pointA.pointUuid) && wallPoints.includes(pointB.pointUuid);

                        if (hasBothPoints) {
                            removedWall = JSON.parse(JSON.stringify(wall));
                            return false;
                        }
                        return true;
                    });
                });

                return removedWall;
            },

            addDecal: (wallUuid, decal) => set((state) => {
                const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid);
                if (wallToUpdate) {
                    wallToUpdate.decals.push(decal);
                }
            }),

            updateDecal: (decalUuid, decal) => set((state) => {
                for (const wall of state.walls) {
                    const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid);
                    if (decalToUpdate) {
                        Object.assign(decalToUpdate, decal);
                    }
                }
            }),

            removeDecal: (decalUuid) => set((state) => {
                for (const wall of state.walls) {
                    wall.decals = wall.decals.filter(d => d.decalUuid !== decalUuid);
                }
            }),

            updateDecalPosition: (decalUuid, position) => set((state) => {
                for (const wall of state.walls) {
                    const decal = wall.decals.find(d => d.decalUuid === decalUuid);
                    if (decal) {
                        decal.decalPosition = position;
                        break;
                    }
                }
            }),

            updateDecalRotation: (decalUuid, rotation) => set((state) => {
                for (const wall of state.walls) {
                    const decal = wall.decals.find(d => d.decalUuid === decalUuid);
                    if (decal) {
                        decal.decalRotation = rotation;
                        break;
                    }
                }
            }),

            updateDecalScale: (decalUuid, scale) => set((state) => {
                for (const wall of state.walls) {
                    const decal = wall.decals.find(d => d.decalUuid === decalUuid);
                    if (decal) {
                        decal.decalScale = scale;
                        break;
                    }
                }
            }),

            removePoint: (pointUuid) => {
                const removedWalls: Wall[] = [];
                set((state) => {
                    state.walls = state.walls.filter((wall) => {
                        const hasPoint = wall.points.some(p => p.pointUuid === pointUuid);
                        if (hasPoint) {
                            removedWalls.push(JSON.parse(JSON.stringify(wall)));
                            return false;
                        }
                        return true;
                    });
                });
                return removedWalls;
            },

            setPosition: (pointUuid, position) => {
                let updatedWalls: Wall[] = [];
                set((state) => {
                    for (const wall of state.walls) {
                        const point = wall.points.find(p => p.pointUuid === pointUuid);
                        if (point) {
                            point.position = position;
                            updatedWalls.push(wall);
                        }
                    }
                });
                return updatedWalls;
            },

            setLayer: (pointUuid, layer) => set((state) => {
                for (const wall of state.walls) {
                    const point = wall.points.find(p => p.pointUuid === pointUuid);
                    if (point) {
                        point.layer = layer;
                    }
                }
            }),

            getWallById: (uuid) => {
                return get().walls.find(w => w.wallUuid === uuid);
            },

            getWallsByPointId: (uuid) => {
                return get().walls.filter((a) => {
                    return a.points.some((p) => p.pointUuid === uuid);
                })
            },

            getWallByPoints: (point) => {
                for (const wall of get().walls) {
                    if (((wall.points[0].pointUuid === point[0].pointUuid) || (wall.points[1].pointUuid === point[0].pointUuid)) &&
                        ((wall.points[0].pointUuid === point[1].pointUuid) || (wall.points[1].pointUuid === point[1].pointUuid))) {
                        return wall;
                    }
                }
                return undefined;
            },

            getWallPointById: (uuid) => {
                for (const wall of get().walls) {
                    const point = wall.points.find(p => p.pointUuid === uuid);
                    if (point) return point;
                }
                return undefined;
            },

            getConnectedPoints: (uuid) => {
                const connected: Point[] = [];
                for (const wall of get().walls) {
                    for (const point of wall.points) {
                        if (point.pointUuid === uuid) {
                            connected.push(...wall.points.filter(p => p.pointUuid !== uuid));
                        }
                    }
                }
                return connected;
            },
        }))
    )
}

export type WallStoreType = ReturnType<typeof createWallStore>;