import React, { useRef, useMemo, useCallback, useState } from "react";
import { InfoIcon, AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as Types from '../../../../types/world/worldTypes';
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import { handleResize } from "../../../../functions/handleResizePannel";

interface ConnectedModel {
    modelUUID: string;
    modelName: string;
    points: {
        uuid: string;
        position: [number, number, number];
        index?: number;
    }[];
    triggers?: {
        uuid: string;
        name: string;
        type: string;
        isUsed: boolean;
    }[];
}

const ArmBotMechanics: React.FC = () => {
    const { selectedActionSphere } = useSelectedActionSphere();
    const { simulationStates, setSimulationStates } = useSimulationStates();
    const { socket } = useSocketStore();
    const [selectedProcessIndex, setSelectedProcessIndex] = useState<number | null>(null);
    const actionsContainerRef = useRef<HTMLDivElement>(null);

    // Get connected models and their triggers
    const connectedModels = useMemo<ConnectedModel[]>(() => {
        if (!selectedActionSphere?.points?.uuid) return [];

        const armBotPaths = simulationStates.filter(
            (path): path is Types.ArmBotEventsSchema => path.type === "ArmBot"
        );

        const currentPoint = armBotPaths.find(
            (path) => path.points.uuid === selectedActionSphere.points.uuid
        )?.points;

        if (!currentPoint?.connections?.targets) return [];

        return currentPoint.connections.targets.reduce<ConnectedModel[]>((acc, target) => {
            const connectedModel = simulationStates.find(
                (model) => model.modeluuid === target.modelUUID
            );

            if (!connectedModel) return acc;

            let triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] = [];
            let points: { uuid: string; position: [number, number, number] }[] = [];

            if (connectedModel.type === "Conveyor") {
                const conveyor = connectedModel as Types.ConveyorEventsSchema;

                const connectedPointUUIDs = currentPoint?.connections?.targets
                    .filter(t => t.modelUUID === connectedModel.modeluuid)
                    .map(t => t.pointUUID) || [];

                points = conveyor.points
                    .map((point, idx) => ({
                        uuid: point.uuid,
                        position: point.position,
                        index: idx
                    }))
                    .filter(point => connectedPointUUIDs.includes(point.uuid));


                triggers = conveyor.points.flatMap(p => p.triggers?.filter(t => t.isUsed) || []);
            }
            else if (connectedModel.type === "StaticMachine") {
                const staticMachine = connectedModel as Types.StaticMachineEventsSchema;

                points = [{
                    uuid: staticMachine.points.uuid,
                    position: staticMachine.points.position
                }];

                triggers = staticMachine.points.triggers ?
                    [{
                        uuid: staticMachine.points.triggers.uuid,
                        name: staticMachine.points.triggers.name,
                        type: staticMachine.points.triggers.type,
                        isUsed: true // StaticMachine triggers are always considered used
                    }] : [];
            }

            if (!acc.some(m => m.modelUUID === connectedModel.modeluuid)) {
                acc.push({
                    modelUUID: connectedModel.modeluuid,
                    modelName: connectedModel.modelName,
                    points,
                    triggers
                });
            }

            return acc;
        }, []);
    }, [selectedActionSphere, simulationStates]);

    // Get triggers from connected models
    const connectedTriggers = useMemo(() => {
        return connectedModels.flatMap(model =>
            (model.triggers || []).map(trigger => ({
                ...trigger,
                displayName: `${model.modelName} - ${trigger.name}`,
                modelUUID: model.modelUUID
            }))
        );
    }, [connectedModels]);

    // Get all points from connected models
    const connectedPoints = useMemo(() => {
        return connectedModels.flatMap(model =>
            model.points.map(point => ({
                ...point,
                displayName: `${model.modelName} - Point${typeof point.index === 'number' ? ` ${point.index}` : ''}`,
                modelUUID: model.modelUUID
            }))
        );
    }, [connectedModels]);


    const { selectedPoint } = useMemo(() => {
        if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null };

        const armBotPaths = simulationStates.filter(
            (path): path is Types.ArmBotEventsSchema => path.type === "ArmBot"
        );

        const points = armBotPaths.find(
            (path) => path.points.uuid === selectedActionSphere.points.uuid
        )?.points;

        return {
            selectedPoint: points || null
        };
    }, [selectedActionSphere, simulationStates]);

    const updateBackend = async (updatedPath: Types.ArmBotEventsSchema | undefined) => {
        if (!updatedPath) return;
        const email = localStorage.getItem("email");
        const organization = email ? email.split("@")[1].split(".")[0] : "";

        const data = {
            organization: organization,
            modeluuid: updatedPath.modeluuid,
            eventData: { type: "ArmBot", points: updatedPath.points }
        }
        console.log('data: ', data);

        socket.emit('v2:model-asset:updateEventData', data);
    }

    const handleActionUpdate = useCallback((updatedAction: Partial<Types.ArmBotEventsSchema['points']['actions']>) => {
        if (!selectedActionSphere?.points?.uuid || !selectedPoint) return;

        const updatedPaths = simulationStates.map((path) => {
            if (path.type === "ArmBot" && path.points.uuid === selectedActionSphere.points.uuid) {
                return {
                    ...path,
                    points: {
                        ...path.points,
                        actions: {
                            ...path.points.actions,
                            ...updatedAction
                        }
                    }
                };
            }
            return path;
        });

        const updatedPath = updatedPaths.find(
            (path): path is Types.ArmBotEventsSchema =>
                path.type === "ArmBot" &&
                path.points.uuid === selectedActionSphere.points.uuid
        );
        updateBackend(updatedPath);

        setSimulationStates(updatedPaths);
    }, [selectedActionSphere?.points?.uuid, selectedPoint, simulationStates, setSimulationStates]);

    const handleSpeedChange = useCallback((speed: number) => {
        handleActionUpdate({ speed });
    }, [handleActionUpdate]);

    const handleProcessChange = useCallback((processes: Types.ArmBotEventsSchema['points']['actions']['processes']) => {
        handleActionUpdate({ processes });
    }, [handleActionUpdate]);

    const handleAddProcess = useCallback(() => {
        if (!selectedPoint) return;

        const newProcess: any = {
            triggerId: "",
            startPoint: "",
            endPoint: ""
        };

        const updatedProcesses = selectedPoint.actions.processes ? [...selectedPoint.actions.processes, newProcess] : [newProcess];

        handleProcessChange(updatedProcesses);
        setSelectedProcessIndex(updatedProcesses.length - 1);
    }, [selectedPoint, handleProcessChange]);

    const handleDeleteProcess = useCallback((index: number) => {
        if (!selectedPoint?.actions.processes) return;

        const updatedProcesses = [...selectedPoint.actions.processes];
        updatedProcesses.splice(index, 1);

        handleProcessChange(updatedProcesses);

        // Reset selection if deleting the currently selected process
        if (selectedProcessIndex === index) {
            setSelectedProcessIndex(null);
        } else if (selectedProcessIndex !== null && selectedProcessIndex > index) {
            // Adjust selection index if needed
            setSelectedProcessIndex(selectedProcessIndex - 1);
        }
    }, [selectedPoint, selectedProcessIndex, handleProcessChange]);

    const handleTriggerSelect = useCallback((displayName: string, index: number) => {
        const selected = connectedTriggers.find(t => t.displayName === displayName);
        if (!selected || !selectedPoint?.actions.processes) return;

        const oldProcess = selectedPoint.actions.processes[index];

        const updatedProcesses = [...selectedPoint.actions.processes];

        // Only reset start/end if new trigger invalidates them (your logic can expand this)
        updatedProcesses[index] = {
            ...oldProcess,
            triggerId: selected.uuid,
            startPoint: oldProcess.startPoint || "", // preserve if exists
            endPoint: oldProcess.endPoint || ""      // preserve if exists
        };

        handleProcessChange(updatedProcesses);
    }, [connectedTriggers, selectedPoint, handleProcessChange]);

    const handleStartPointSelect = useCallback((displayName: string, index: number) => {
        if (!selectedPoint?.actions.processes) return;

        const point = connectedPoints.find(p => p.displayName === displayName);
        if (!point) return;

        const updatedProcesses = [...selectedPoint.actions.processes];
        updatedProcesses[index] = {
            ...updatedProcesses[index],
            startPoint: point.uuid
        };

        handleProcessChange(updatedProcesses);
    }, [selectedPoint, connectedPoints, handleProcessChange]);

    const handleEndPointSelect = useCallback((displayName: string, index: number) => {
        if (!selectedPoint?.actions.processes) return;

        const point = connectedPoints.find(p => p.displayName === displayName);
        if (!point) return;

        const updatedProcesses = [...selectedPoint.actions.processes];
        updatedProcesses[index] = {
            ...updatedProcesses[index],
            endPoint: point.uuid
        };

        handleProcessChange(updatedProcesses);
    }, [selectedPoint, connectedPoints, handleProcessChange]);

    const getProcessByIndex = useCallback((index: number) => {
        if (!selectedPoint?.actions.processes || index >= selectedPoint.actions.processes.length) return null;
        return selectedPoint.actions.processes[index];
    }, [selectedPoint]);

    const getFilteredTriggerOptions = (currentIndex: number) => {
        const usedTriggerUUIDs = selectedPoint?.actions.processes?.filter((_, i) => i !== currentIndex).map(p => p.triggerId).filter(Boolean) ?? [];

        return connectedTriggers.filter(trigger => !usedTriggerUUIDs.includes(trigger.uuid)).map(trigger => trigger.displayName);
    };

    return (
        <div className="machine-mechanics-container" key={selectedPoint?.uuid}>
            <div className="machine-mechanics-header">
                {selectedActionSphere?.path?.modelName || "ArmBot point not found"}
            </div>

            <div className="machine-mechanics-content-container">
                <div className="selected-properties-container">
                    <div className="properties-header">ArmBot Properties</div>

                    {selectedPoint && (
                        <>
                            <InputWithDropDown
                                key={`speed-${selectedPoint.uuid}`}
                                label="ArmBot Speed"
                                value={selectedPoint.actions.speed.toString()}
                                onChange={(value) => handleSpeedChange(parseInt(value))}
                            />

                            <div className="actions">
                                <div className="header">
                                    <div className="header-value">Processes</div>
                                    <div className="add-button" onClick={handleAddProcess}>
                                        <AddIcon /> Add
                                    </div>
                                </div>
                                <div
                                    className="lists-main-container"
                                    ref={actionsContainerRef}
                                    style={{ height: "120px" }}
                                >
                                    <div className="list-container">
                                        {selectedPoint.actions.processes?.map((process, index) => (
                                            <div
                                                key={`process-${index}`}
                                                className={`list-item ${selectedProcessIndex === index ? "active" : ""}`}
                                            >
                                                <div
                                                    className="value"
                                                    onClick={() => setSelectedProcessIndex(index)}
                                                >
                                                    Process {index + 1}
                                                </div>
                                                <div
                                                    className="remove-button"
                                                    onClick={() => handleDeleteProcess(index)}
                                                >
                                                    <RemoveIcon />
                                                </div>
                                            </div>
                                        ))}
                                    </div>
                                    <div
                                        className="resize-icon"
                                        id="action-resize"
                                        onMouseDown={(e) => handleResize(e, actionsContainerRef)}
                                    >
                                        <ResizeHeightIcon />
                                    </div>
                                </div>
                            </div>

                            {selectedProcessIndex !== null && (
                                <div className="process-configuration">
                                    <LabledDropdown
                                        key={`trigger-select-${selectedProcessIndex}`}
                                        label="Select Trigger"
                                        defaultOption={
                                            connectedTriggers.find(t =>
                                                t.uuid === getProcessByIndex(selectedProcessIndex)?.triggerId
                                            )?.displayName || 'Select a trigger'
                                        }
                                        onSelect={(value) => handleTriggerSelect(value, selectedProcessIndex)}
                                        options={getFilteredTriggerOptions(selectedProcessIndex)}
                                    />

                                    <LabledDropdown
                                        key={`start-point-${selectedProcessIndex}`}
                                        label="Start Point"
                                        defaultOption={
                                            connectedPoints.find(p =>
                                                p.uuid === getProcessByIndex(selectedProcessIndex)?.startPoint
                                            )?.displayName || 'Select start point'
                                        }
                                        onSelect={(value) => handleStartPointSelect(value, selectedProcessIndex)}
                                        options={connectedPoints.map(point => point.displayName)}
                                    />

                                    <LabledDropdown
                                        key={`end-point-${selectedProcessIndex}`}
                                        label="End Point"
                                        defaultOption={
                                            connectedPoints.find(p =>
                                                p.uuid === getProcessByIndex(selectedProcessIndex)?.endPoint
                                            )?.displayName || 'Select end point'
                                        }
                                        onSelect={(value) => handleEndPointSelect(value, selectedProcessIndex)}
                                        options={connectedPoints.map(point => point.displayName)}
                                    />
                                </div>
                            )}
                        </>
                    )}
                </div>

                <div className="footer">
                    <InfoIcon />
                    Configure ArmBot properties and trigger-based processes.
                </div>
            </div>
        </div>
    );
};

export default React.memo(ArmBotMechanics);