import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { DragDropContext } from "../dnd/lib";
import PropTypes from "prop-types";
import Lane from "./Lane";
import { PopoverWrapper } from "react-popopo";
import * as actions from "../../../../redux/trello/trello.actions.js";
import { BoardWrapper } from "../styles/Base.js";
import ProductionStatistics from "../../production-board-kanban.statistics.jsx";
import isEqual from "lodash/isEqual";

const useDragMap = () => {
  const dragMapRef = useRef(new Map());

  const setDragTime = (laneId) => {
    dragMapRef.current.set(laneId, Date.now());
  };

  const getLastDragTime = (laneId) => {
    return dragMapRef.current.get(laneId);
  };

  return { setDragTime, getLastDragTime };
};

const BoardContainer = ({
  data,
  onDataChange = () => {
    // NO OP
  },
  onDragEnd = () => {
    // NO OP
  },
  laneSortFunction = () => {
    // NO OP
  },
  orientation = "horizontal",
  cardSettings = {},
  eventBusHandle,
  queryData
}) => {
  const [isDragging, setIsDragging] = useState(false);
  const [isProcessing, setIsProcessing] = useState(false);
  const [maxLaneHeight, setMaxLaneHeight] = useState(0);
  const [maxCardHeight, setMaxCardHeight] = useState(0);
  const [maxCardWidth, setMaxCardWidth] = useState(0);

  const dispatch = useDispatch();
  const currentReducerData = useSelector((state) => (state.trello.lanes ? state.trello : {}));
  const { setDragTime, getLastDragTime } = useDragMap();
  const previousDataRef = useRef(null);

  const wireEventBus = useCallback(() => {
    const eventBus = {
      publish: (event) => {
        switch (event.type) {
          case "MOVE_CARD":
            return dispatch(
              actions.moveCardAcrossLanes({
                fromLaneId: event.fromLaneId,
                toLaneId: event.toLaneId,
                cardId: event.cardId,
                index: event.index,
                event
              })
            );
          default:
            return;
        }
      }
    };
    eventBusHandle(eventBus);
  }, [dispatch, eventBusHandle]);

  useEffect(() => {
    if (!isEqual(previousDataRef.current, data)) {
      previousDataRef.current = data;
      dispatch(actions.loadBoard(data));
      if (eventBusHandle) {
        wireEventBus();
      }
    }
  }, [data, wireEventBus, dispatch]);

  useEffect(() => {
    onDataChange(currentReducerData);
  }, [currentReducerData, onDataChange]);

  const onDragStart = () => {
    setIsDragging(true);
  };

  const onLaneDrag = async ({ draggableId, type, source, reason, mode, destination, combine }) => {
    setIsDragging(false);

    // Validate drag type and source
    if (type !== "lane" || !source) {
      // Invalid drag type or missing source, attempt to revert if possible
      if (source) {
        dispatch(
          actions.moveCardAcrossLanes({
            fromLaneId: source.droppableId,
            toLaneId: source.droppableId,
            cardId: draggableId,
            index: source.index
          })
        );
      }
      setIsProcessing(false);
      try {
        await onDragEnd({ draggableId, type, source, reason, mode, destination, combine });
      } catch (err) {
        console.error("Error in onLaneDrag for invalid drag type or source", err);
      }
      return;
    }

    setDragTime(source.droppableId);
    setIsProcessing(true);

    // Handle valid drop to a different lane or position
    if (destination && (source.droppableId !== destination.droppableId || source.index !== destination.index)) {
      dispatch(
        actions.moveCardAcrossLanes({
          fromLaneId: source.droppableId,
          toLaneId: destination.droppableId,
          cardId: draggableId,
          index: destination.index
        })
      );
    } else {
      // Same-lane drop or no destination, revert to original position
      dispatch(
        actions.moveCardAcrossLanes({
          fromLaneId: source.droppableId,
          toLaneId: source.droppableId,
          cardId: draggableId,
          index: source.index
        })
      );
    }

    try {
      await onDragEnd({ draggableId, type, source, reason, mode, destination, combine });
    } catch (err) {
      console.error("Error in onLaneDrag", err);
      // Ensure revert on error
      dispatch(
        actions.moveCardAcrossLanes({
          fromLaneId: source.droppableId,
          toLaneId: source.droppableId,
          cardId: draggableId,
          index: source.index
        })
      );
    } finally {
      setIsProcessing(false);
    }
  };

  const boardKey = useMemo(() => {
    // React Compiler: Generate stable key from lane IDs
    return currentReducerData.lanes?.map((l) => l.id).join("-") || "empty";
  }, [currentReducerData.lanes]);

  return (
    <div>
      <ProductionStatistics data={queryData} reducerData={currentReducerData} cardSettings={cardSettings} />
      <PopoverWrapper>
        <BoardWrapper orientation={orientation}>
          <DragDropContext key={boardKey} onDragEnd={onLaneDrag} onDragStart={onDragStart} contextId="production-board">
            {currentReducerData.lanes.map((lane, index) => (
              <Lane
                key={lane.id}
                id={lane.id}
                title={lane.title}
                index={index}
                laneSortFunction={laneSortFunction}
                orientation={orientation}
                cards={lane.cards}
                isDragging={isDragging}
                isProcessing={isProcessing}
                cardSettings={cardSettings}
                maxLaneHeight={maxLaneHeight}
                setMaxLaneHeight={setMaxLaneHeight}
                maxCardHeight={maxCardHeight}
                setMaxCardHeight={setMaxCardHeight}
                maxCardWidth={maxCardWidth}
                setMaxCardWidth={setMaxCardWidth}
                lastDrag={getLastDragTime(lane.id)}
              />
            ))}
          </DragDropContext>
        </BoardWrapper>
      </PopoverWrapper>
    </div>
  );
};

BoardContainer.propTypes = {
  id: PropTypes.string,
  data: PropTypes.object.isRequired,
  reducerData: PropTypes.object,
  onDataChange: PropTypes.func,
  eventBusHandle: PropTypes.func,
  laneSortFunction: PropTypes.func,
  handleDragEnd: PropTypes.func,
  orientation: PropTypes.string
};

export default BoardContainer;
