import { DragDropContext, type DragStart, type DropResult } from "@hello-pangea/dnd";
import { useQueryClient } from "@tanstack/react-query";
import { useEffect, useState, type FC } from "react";
import { useParams } from "react-router-dom";
import { toast } from '../../components/Toast';
import PageLoading from "../../components/PageLoading";
import { ErrorType } from "../../helpers/types";
import AddNewColumn from "./components/AddNewColumn";
import Column from "./components/Column";
import HeaderWorkspace from "./components/HeaderWorkspace";
import TaskDetailModal from "./components/task-detail/TaskDetailModal";
import { useGetProject, useGetProjectDetail } from "./project/hooks/useProjectData";
import ProjectSettingsPanel from "./project/components/ProjectSettingsPanel";
import type { ProjectDetailType } from "./project/types/ProjectTypes";
import { getProjectBackgroundStyle } from "./project/utils/getProjectBackgroundStyle";
import { useUpdateTaskPhase } from "./task-phase/hooks/useTaskPhaseData";
import type { TaskPhaseItemType } from "./task-phase/types/TaskPhaseTypes";
import { useChangeTaskPhase, useChangeTaskPriority } from "./task/hooks/useTaskData";
import type { TaskItemType } from "./task/types/TaskTypes";
import { mapTaskItemToTask } from "./task/utils/mapTaskItemToTask";
import type { Column as ColumnType, Task } from "./types";
import { getPriorityDestinationTaskId } from "./utils/getPriorityDestinationTaskId";
import { reorderColumns } from "./utils/reorderColumns";
import { reorderTaskItems } from "./utils/reorderTaskItems";
import { getTaskPhaseItems, insertTaskPhaseItem, updateTaskPhaseItems, type TasksByTaskPhaseInfiniteData } from "./utils/taskPhaseQueryCache";

const Workspace: FC = () => {
  const queryClient = useQueryClient();
  const { slug: projectId = "" } = useParams<{ slug: string }>();
  const { data: project, isPending } = useGetProject(projectId);
  const { data: projectDetailData } = useGetProjectDetail(projectId);
  const projectDetail: ProjectDetailType | undefined =
    projectDetailData?.data?.project ?? projectDetailData?.data;
  const { mutate: changeTaskPhase } = useChangeTaskPhase();
  const { mutate: changeTaskPriority } = useChangeTaskPriority();
  const { mutate: updateTaskPhase } = useUpdateTaskPhase();
  const [columns, setColumns] = useState<ColumnType[]>([]);
  const [draggedTask, setDraggedTask] = useState<Task | null>(null);
  const [draggedColumnId, setDraggedColumnId] = useState<string | null>(null);
  const [selectedTask, setSelectedTask] = useState<Task | null>(null);
  const [isSettingsOpen, setIsSettingsOpen] = useState(false);

  useEffect(() => {
    if (!project?.data?.taskPhases) return;

    setColumns(
      [...project.data.taskPhases]
        .sort((a, b) => a.order - b.order)
        .map((phase) => ({
          id: phase.id,
          title: phase.name,
          color: phase.color,
          order: phase.order,
        })),
    );
  }, [project?.data?.taskPhases]);

  const handleTaskDragStart = (start: DragStart) => {
    if (start.type !== "TASK") return;

    const sourceColumnId = start.source.droppableId;
    const previousData = queryClient.getQueryData<TasksByTaskPhaseInfiniteData>(["tasks-by-task-phase", sourceColumnId]);
    const items = getTaskPhaseItems(previousData);
    const item = items.find((task) => task.id === start.draggableId);
    if (!item) return;

    setDraggedTask(mapTaskItemToTask(item as TaskItemType, sourceColumnId, start.source.index));
  };

  const handleDrop = (columnId: string, index: number, activeTask: Task) => {
    const sourceColumnId = activeTask.columnId;
    const columnChanged = sourceColumnId !== columnId;

    if (!columnChanged) {
      const previousData = queryClient.getQueryData<TasksByTaskPhaseInfiniteData>(["tasks-by-task-phase", columnId]);
      const currentItems = getTaskPhaseItems(previousData);
      const destTaskId = getPriorityDestinationTaskId(currentItems, activeTask.id, index);

      if (!destTaskId) return;

      queryClient.setQueryData<TasksByTaskPhaseInfiniteData>(["tasks-by-task-phase", columnId], (current) =>
        updateTaskPhaseItems(current, (items) => reorderTaskItems(items, activeTask.id, index)),
      );

      changeTaskPriority(
        { srcId: activeTask.id, destId: destTaskId, taskPhaseId: columnId, projectId },
        {
          onError: (error: ErrorType) => {
            if (previousData) {
              queryClient.setQueryData(["tasks-by-task-phase", columnId], previousData);
            }
            toast(error.response?.data?.error.message[0], 'error');
          },
        },
      );
      return;
    }

    const previousSource = queryClient.getQueryData<TasksByTaskPhaseInfiniteData>(["tasks-by-task-phase", sourceColumnId]);
    const previousDest = queryClient.getQueryData<TasksByTaskPhaseInfiniteData>(["tasks-by-task-phase", columnId]);

    queryClient.setQueryData<TasksByTaskPhaseInfiniteData>(["tasks-by-task-phase", sourceColumnId], (current) =>
      updateTaskPhaseItems(current, (items) => items.filter((task) => task.id !== activeTask.id)),
    );

    queryClient.setQueryData<TasksByTaskPhaseInfiniteData>(["tasks-by-task-phase", columnId], (current) =>
      insertTaskPhaseItem(
        current,
        {
          id: activeTask.id,
          title: activeTask.title,
          taskPhaseId: columnId,
          order: index,
          priority: index,
          tag: activeTask.tag,
          dateRange: activeTask.dateRange,
          color: activeTask.color,
          attachments: activeTask.attachments,
          checklistDone: activeTask.checklistDone,
          checklistTotal: activeTask.checklistTotal,
        },
        index,
      ),
    );

    const destTaskId = getPriorityDestinationTaskId(getTaskPhaseItems(previousDest), activeTask.id, index, false);

    if (selectedTask?.id === activeTask.id) {
      setSelectedTask({ ...activeTask, columnId });
    }

    changeTaskPhase(
      { id: activeTask.id, params: { taskPhaseId: columnId }, projectId },
      {
        onSuccess: () => {
          if (destTaskId) {
            changeTaskPriority(
              { srcId: activeTask.id, destId: destTaskId, taskPhaseId: columnId, projectId },
              {
                onError: (error: ErrorType) => {
                  queryClient.invalidateQueries({ queryKey: ["tasks-by-task-phase", sourceColumnId] });
                  queryClient.invalidateQueries({ queryKey: ["tasks-by-task-phase", columnId] });
                  toast(error.response?.data?.error.message[0], 'error');
                },
              },
            );
            return;
          }

          queryClient.invalidateQueries({ queryKey: ["tasks-by-task-phase", sourceColumnId] });
          queryClient.invalidateQueries({ queryKey: ["tasks-by-task-phase", columnId] });
        },
        onError: (error: ErrorType) => {
          if (previousSource) {
            queryClient.setQueryData(["tasks-by-task-phase", sourceColumnId], previousSource);
          }
          if (previousDest) {
            queryClient.setQueryData(["tasks-by-task-phase", columnId], previousDest);
          }
          if (selectedTask?.id === activeTask.id) {
            setSelectedTask(activeTask);
          }
          toast(error.response?.data?.error.message[0], 'error');
        },
      },
    );
  };

  const handleTaskDragEnd = (result: DropResult) => {
    const activeTask = draggedTask;
    setDraggedTask(null);

    if (!activeTask || !result.destination || result.type !== "TASK") return;

    const { source, destination } = result;
    if (source.droppableId === destination.droppableId && source.index === destination.index) return;

    handleDrop(destination.droppableId, destination.index, activeTask);
  };

  const handleTaskPhaseChanged = (taskId: string, taskPhaseId: string) => {
    const sourceColumnId = selectedTask?.columnId;

    if (sourceColumnId) {
      queryClient.invalidateQueries({ queryKey: ["tasks-by-task-phase", sourceColumnId] });
    }
    queryClient.invalidateQueries({ queryKey: ["tasks-by-task-phase", taskPhaseId] });

    setSelectedTask((prev) => (prev?.id === taskId ? { ...prev, columnId: taskPhaseId } : prev));
  };

  const handleColumnDragStart = (columnId: string) => {
    setDraggedColumnId(columnId);
  };

  const handleColumnDragEnd = () => {
    setDraggedColumnId(null);
  };

  const handleColumnDrop = (overColumnId: string) => {
    if (!draggedColumnId || draggedColumnId === overColumnId) return;

    const previousColumns = columns;
    const nextColumns = reorderColumns(columns, draggedColumnId, overColumnId);
    const movedColumn = nextColumns.find((column) => column.id === draggedColumnId);

    setColumns(nextColumns);
    setDraggedColumnId(null);

    if (!movedColumn?.order) return;

    updateTaskPhase(
      { id: draggedColumnId, params: { order: movedColumn.order }, projectId },
      {
        onError: (error: ErrorType) => {
          setColumns(previousColumns);
          toast(error.response?.data?.error.message[0], 'error');
        },
      },
    );
  };

  const nextColumnOrder = columns.length ? Math.max(...columns.map((column) => column.order ?? 0)) + 1 : 1;

  const handleColumnCreated = (phase: TaskPhaseItemType) => {
    setColumns((prev) => [...prev, { id: phase.id, title: phase.name, color: phase.color, order: phase.order }].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)));
  };

  const handleColumnDeleted = (columnId: string) => {
    setColumns((prev) => prev.filter((column) => column.id !== columnId));
    queryClient.removeQueries({ queryKey: ["tasks-by-task-phase", columnId] });
    if (selectedTask?.columnId === columnId) {
      setSelectedTask(null);
    }
  };

  const handleColumnTitleUpdated = (columnId: string, title: string) => {
    setColumns((prev) => prev.map((column) => (column.id === columnId ? { ...column, title } : column)));
  };

  if (isPending) {
    return <PageLoading />;
  }

  return (
    <div
      className="h-full min-h-0 rounded-none xl:rounded-[32px] overflow-hidden flex flex-col"
      style={getProjectBackgroundStyle({
        backgroundColor: projectDetail?.backgroundColor,
        backgroundPicture: projectDetail?.backgroundPicture,
      })}
    >
      <HeaderWorkspace
        projectName={project?.data?.name ?? ""}
        onSettingsClick={() => setIsSettingsOpen(true)}
      />
      <div className="flex-1 min-h-0 flex flex-col px-3 sm:px-4 xl:px-8 py-4 xl:py-6">
        <DragDropContext onDragStart={handleTaskDragStart} onDragEnd={handleTaskDragEnd}>
          <div className="flex-1 min-h-0 overflow-x-auto overflow-y-hidden overscroll-x-contain">
            <div className="flex gap-3 sm:gap-4 xl:gap-7 h-full min-h-0 items-stretch w-max min-w-full">
              {columns.map((column) => (
                <Column
                  key={column.id}
                  column={column}
                  projectId={projectId}
                  draggedColumnId={draggedColumnId}
                  onColumnDragStart={handleColumnDragStart}
                  onColumnDragEnd={handleColumnDragEnd}
                  onColumnDrop={handleColumnDrop}
                  onTaskClick={setSelectedTask}
                  onColumnDeleted={handleColumnDeleted}
                  onColumnTitleUpdated={handleColumnTitleUpdated}
                />
              ))}

              <AddNewColumn
                projectId={projectId}
                nextOrder={nextColumnOrder}
                onColumnCreated={handleColumnCreated}
              />
            </div>
          </div>
        </DragDropContext>
      </div>

      <TaskDetailModal
        open={selectedTask !== null}
        task={selectedTask}
        columns={columns}
        projectId={projectId}
        onClose={() => setSelectedTask(null)}
        onTaskPhaseChanged={handleTaskPhaseChanged}
      />

      <ProjectSettingsPanel
        open={isSettingsOpen}
        onClose={() => setIsSettingsOpen(false)}
        projectId={projectId}
        project={projectDetail}
      />
    </div>
  );
};

export default Workspace;
