"use client";

import type { GanttTask, GanttChartProps, TimelineRange, GanttBarPosition } from "./types";
import { TaskStatus } from "@mosaic/shared";
import { formatDate, isPastTarget, isApproachingTarget } from "@/lib/utils/date-format";
import { useMemo, useCallback } from "react";
import styles from "./gantt.module.css";

/**
 * Represents a dependency line between two tasks
 */
interface DependencyLine {
  fromTaskId: string;
  toTaskId: string;
  fromX: number;
  fromY: number;
  toX: number;
  toY: number;
}

/**
 * Calculate the timeline range from a list of tasks
 */
function calculateTimelineRange(tasks: GanttTask[]): TimelineRange {
  if (tasks.length === 0) {
    const now = new Date();
    const oneMonthLater = new Date(now);
    oneMonthLater.setMonth(oneMonthLater.getMonth() + 1);

    return {
      start: now,
      end: oneMonthLater,
      totalDays: 30,
    };
  }

  const firstTask = tasks[0];
  if (!firstTask) {
    // This should not happen due to the check above, but makes TS happy
    const now = new Date();
    const oneMonthLater = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
    return {
      start: now,
      end: oneMonthLater,
      totalDays: 30,
    };
  }

  let earliest = firstTask.startDate;
  let latest = firstTask.endDate;

  tasks.forEach((task): void => {
    if (task.startDate < earliest) {
      earliest = task.startDate;
    }
    if (task.endDate > latest) {
      latest = task.endDate;
    }
  });

  // Add padding (5% on each side)
  const totalMs = latest.getTime() - earliest.getTime();
  const padding = totalMs * 0.05;

  const start = new Date(earliest.getTime() - padding);
  const end = new Date(latest.getTime() + padding);

  const totalDays = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));

  return { start, end, totalDays };
}

/**
 * Calculate the position and width for a task bar
 */
function calculateBarPosition(
  task: GanttTask,
  timelineRange: TimelineRange,
  rowIndex: number
): Required<GanttBarPosition> {
  const { start: rangeStart, totalDays } = timelineRange;

  const taskStartOffset = Math.max(
    0,
    (task.startDate.getTime() - rangeStart.getTime()) / (1000 * 60 * 60 * 24)
  );

  const taskDuration = Math.max(
    0.5, // Minimum 0.5 day width for visibility
    (task.endDate.getTime() - task.startDate.getTime()) / (1000 * 60 * 60 * 24)
  );

  const leftPercent = (taskStartOffset / totalDays) * 100;
  const widthPercent = (taskDuration / totalDays) * 100;

  const result: GanttBarPosition = {
    left: `${String(leftPercent)}%`,
    width: `${String(widthPercent)}%`,
    top: rowIndex * 48, // 48px row height
  };

  return result;
}

/**
 * Get CSS class for task status
 */
function getStatusClass(status: TaskStatus): string {
  switch (status) {
    case TaskStatus.COMPLETED:
      return "bg-green-500";
    case TaskStatus.IN_PROGRESS:
      return "bg-blue-500";
    case TaskStatus.PAUSED:
      return "bg-yellow-500";
    case TaskStatus.ARCHIVED:
      return "bg-gray-400";
    default:
      return "bg-gray-500";
  }
}

/**
 * Get CSS class for task row status
 */
function getRowStatusClass(status: TaskStatus): string {
  switch (status) {
    case TaskStatus.COMPLETED:
      return styles.rowCompleted ?? "";
    case TaskStatus.IN_PROGRESS:
      return styles.rowInProgress ?? "";
    case TaskStatus.PAUSED:
      return styles.rowPaused ?? "";
    default:
      return "";
  }
}

/**
 * Generate month labels for the timeline header
 */
function generateTimelineLabels(range: TimelineRange): { label: string; position: number }[] {
  const labels: { label: string; position: number }[] = [];
  const current = new Date(range.start);

  // Generate labels for each month in the range
  while (current <= range.end) {
    const position =
      (current.getTime() - range.start.getTime()) / (1000 * 60 * 60 * 24) / range.totalDays;

    const label = current.toLocaleDateString("en-US", {
      month: "short",
      year: "numeric",
    });

    labels.push({ label, position: position * 100 });

    // Move to next month
    current.setMonth(current.getMonth() + 1);
  }

  return labels;
}

/**
 * Calculate dependency lines between tasks
 */
function calculateDependencyLines(
  tasks: GanttTask[],
  timelineRange: TimelineRange
): DependencyLine[] {
  const lines: DependencyLine[] = [];
  const taskIndexMap = new Map<string, number>();

  // Build index map
  tasks.forEach((task, index) => {
    taskIndexMap.set(task.id, index);
  });

  const { start: rangeStart, totalDays } = timelineRange;

  tasks.forEach((task, toIndex) => {
    if (!task.dependencies || task.dependencies.length === 0) {
      return;
    }

    task.dependencies.forEach((depId): void => {
      const fromIndex = taskIndexMap.get(depId);
      if (fromIndex === undefined) {
        return;
      }

      const fromTask = tasks[fromIndex];
      if (!fromTask) {
        return;
      }

      // Calculate positions (as percentages)
      const fromEndOffset = Math.max(
        0,
        (fromTask.endDate.getTime() - rangeStart.getTime()) / (1000 * 60 * 60 * 24)
      );
      const toStartOffset = Math.max(
        0,
        (task.startDate.getTime() - rangeStart.getTime()) / (1000 * 60 * 60 * 24)
      );

      const fromX = (fromEndOffset / totalDays) * 100;
      const toX = (toStartOffset / totalDays) * 100;
      const fromY = fromIndex * 48 + 24; // Center of the row
      const toY = toIndex * 48 + 24;

      lines.push({
        fromTaskId: depId,
        toTaskId: task.id,
        fromX,
        fromY,
        toX,
        toY,
      });
    });
  });

  return lines;
}

/**
 * Main Gantt Chart Component
 */
export function GanttChart({
  tasks,
  onTaskClick,
  height = 400,
  showDependencies = false,
}: GanttChartProps): React.ReactElement {
  // Sort tasks by start date
  const sortedTasks = useMemo(() => {
    return [...tasks].sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
  }, [tasks]);

  // Calculate timeline range
  const timelineRange = useMemo(() => calculateTimelineRange(sortedTasks), [sortedTasks]);

  // Generate timeline labels
  const timelineLabels = useMemo(() => generateTimelineLabels(timelineRange), [timelineRange]);

  // Calculate dependency lines
  const dependencyLines = useMemo(
    () => (showDependencies ? calculateDependencyLines(sortedTasks, timelineRange) : []),
    [showDependencies, sortedTasks, timelineRange]
  );

  const handleTaskClick = useCallback(
    (task: GanttTask) => (): void => {
      if (onTaskClick) {
        onTaskClick(task);
      }
    },
    [onTaskClick]
  );

  const handleKeyDown = useCallback(
    (task: GanttTask) =>
      (e: React.KeyboardEvent<HTMLDivElement>): void => {
        if (e.key === "Enter" || e.key === " ") {
          e.preventDefault();
          if (onTaskClick) {
            onTaskClick(task);
          }
        }
      },
    [onTaskClick]
  );

  return (
    <div
      role="region"
      aria-label="Gantt Chart"
      className="gantt-chart bg-white rounded-lg border border-gray-200 overflow-hidden"
      style={{ height: `${height.toString()}px` }}
    >
      <div className="gantt-container flex h-full">
        {/* Task list column */}
        <div className="gantt-task-list w-64 border-r border-gray-200 overflow-y-auto flex-shrink-0">
          <div className="gantt-task-list-header bg-gray-50 p-3 border-b border-gray-200 font-semibold sticky top-0 z-10">
            Tasks
          </div>
          <div className="gantt-task-list-body">
            {sortedTasks.map((task) => {
              const isPast = isPastTarget(task.endDate);
              const isApproaching = !isPast && isApproachingTarget(task.endDate);

              return (
                <div
                  key={task.id}
                  role="row"
                  className={`gantt-task-row p-3 border-b border-gray-100 h-12 flex items-center ${getRowStatusClass(
                    task.status
                  )}`}
                >
                  <div className="flex-1 min-w-0">
                    <div className="text-sm font-medium text-gray-900 truncate">{task.title}</div>
                    {isPast && task.status !== TaskStatus.COMPLETED && (
                      <div className="text-xs text-amber-600">Target passed</div>
                    )}
                    {isApproaching && task.status !== TaskStatus.COMPLETED && (
                      <div className="text-xs text-orange-600">Approaching target</div>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* Timeline column */}
        <div className="gantt-timeline flex-1 overflow-x-auto overflow-y-auto">
          <div role="region" aria-label="Timeline" className="gantt-timeline-container min-w-full">
            {/* Timeline header */}
            <div className="gantt-timeline-header bg-gray-50 border-b border-gray-200 h-12 sticky top-0 z-10 relative">
              {timelineLabels.map((label, index) => (
                <div
                  key={index}
                  className="absolute top-0 bottom-0 flex items-center text-xs text-gray-600 px-2"
                  style={{ left: `${label.position.toString()}%` }}
                >
                  {label.label}
                </div>
              ))}
            </div>

            {/* Timeline grid and bars */}
            <div className="gantt-timeline-body relative">
              {/* Grid lines */}
              <div className="gantt-grid absolute inset-0 pointer-events-none">
                {timelineLabels.map((label, index) => (
                  <div
                    key={index}
                    className="absolute top-0 bottom-0 w-px bg-gray-200"
                    style={{ left: `${label.position.toString()}%` }}
                  />
                ))}
              </div>

              {/* Dependency lines SVG */}
              {showDependencies && dependencyLines.length > 0 && (
                <svg
                  className="gantt-dependencies absolute inset-0 pointer-events-none overflow-visible"
                  style={{ width: "100%", height: `${(sortedTasks.length * 48).toString()}px` }}
                  aria-hidden="true"
                >
                  <defs>
                    <marker
                      id="arrowhead"
                      markerWidth="10"
                      markerHeight="7"
                      refX="9"
                      refY="3.5"
                      orient="auto"
                    >
                      <polygon points="0 0, 10 3.5, 0 7" fill="#6b7280" />
                    </marker>
                  </defs>
                  {dependencyLines.map((line) => (
                    <path
                      key={`dep-${line.fromTaskId}-${line.toTaskId}`}
                      d={`M ${line.fromX.toString()}% ${line.fromY.toString()} C ${(line.fromX + 2).toString()}% ${line.fromY.toString()}, ${(line.toX - 2).toString()}% ${line.toY.toString()}, ${line.toX.toString()}% ${line.toY.toString()}`}
                      stroke="#6b7280"
                      strokeWidth="2"
                      fill="none"
                      markerEnd="url(#arrowhead)"
                      className="dependency-line"
                    />
                  ))}
                </svg>
              )}

              {/* Task bars and milestones */}
              {sortedTasks.map((task, index) => {
                const position = calculateBarPosition(task, timelineRange, index);
                const statusClass = getStatusClass(task.status);

                // Render milestone as diamond shape
                if (task.isMilestone === true) {
                  return (
                    <div
                      key={task.id}
                      role="button"
                      tabIndex={0}
                      aria-label={`Milestone: ${task.title}, on ${formatDate(task.startDate)}`}
                      className="gantt-milestone absolute cursor-pointer transition-all hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-blue-500"
                      style={{
                        left: position.left,
                        top: `${(position.top + 8).toString()}px`,
                      }}
                      onClick={handleTaskClick(task)}
                      onKeyDown={handleKeyDown(task)}
                    >
                      <div
                        className={`w-6 h-6 transform rotate-45 ${statusClass}`}
                        title={task.title}
                      />
                    </div>
                  );
                }

                return (
                  <div
                    key={task.id}
                    role="button"
                    tabIndex={0}
                    aria-label={`Gantt bar for ${task.title}, from ${formatDate(
                      task.startDate
                    )} to ${formatDate(task.endDate)}`}
                    className={`gantt-bar absolute h-8 rounded cursor-pointer transition-all hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-blue-500 ${statusClass}`}
                    style={{
                      left: position.left,
                      width: position.width,
                      top: `${(position.top + 8).toString()}px`, // Center in row
                    }}
                    onClick={handleTaskClick(task)}
                    onKeyDown={handleKeyDown(task)}
                  >
                    <div className="px-2 text-xs text-white truncate leading-8">{task.title}</div>
                  </div>
                );
              })}

              {/* Spacer for scrolling */}
              <div
                style={{
                  height: `${(sortedTasks.length * 48).toString()}px`,
                }}
              />
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
