import { format } from "date-fns";
import { useCallback, useRef, useState } from "react";
import { createPortal } from "react-dom";

import {
  buildAllocationBarSegment,
  dateStringsFromDayIndices,
  dayIndexFromPointer,
  projectBarStyle,
} from "../../planner/allocationTimeline";
import type { Milestone, Project, ProjectPhase } from "../../types";
import { MilestoneAddPopover } from "./MilestoneAddPopover";
import {
  DEFAULT_PHASE_COLOR,
  PhaseCreatePopover,
  phasePopoverAnchorFromTrack,
} from "./PhaseCreatePopover";

type TrackMode = "project-line" | "phases";

interface ProjectCalendarTrackProps {
  mode: TrackMode;
  project: Project;
  dayDates: Date[];
  showTentative: boolean;
  /** When false, the row shows milestones only (schedule dates live in the project drawer). */
  showScheduleSpan?: boolean;
  enableMilestones?: boolean;
  enablePhaseDrag?: boolean;
  onCreateMilestone: (dueDate: string, name: string, note?: string) => void;
  onCreatePhase: (input: {
    name: string;
    start_date: string;
    end_date: string;
    color?: string;
  }) => void;
  isSaving?: boolean;
}

export function ProjectCalendarTrack({
  mode,
  project,
  dayDates,
  showTentative,
  showScheduleSpan = false,
  enableMilestones = false,
  enablePhaseDrag = false,
  onCreateMilestone,
  onCreatePhase,
  isSaving = false,
}: ProjectCalendarTrackProps) {
  const trackRef = useRef<HTMLDivElement>(null);
  const dragRangeRef = useRef<{ start: number; end: number } | null>(null);
  const [dragRange, setDragRange] = useState<{ start: number; end: number } | null>(null);
  const [popover, setPopover] = useState<
    | { type: "milestone"; dueDate: string; anchor: { top: number; left: number } }
    | { type: "phase"; startDate: string; endDate: string; anchor: { top: number; left: number } }
    | null
  >(null);
  const [phasePreviewColor] = useState(DEFAULT_PHASE_COLOR);
  const [hoveredDayIndex, setHoveredDayIndex] = useState<number | null>(null);

  const totalDays = dayDates.length;
  const lineSegment =
    mode === "project-line"
      ? buildAllocationBarSegment(dayDates, project.start_date, project.end_date)
      : null;

  const previewSegment =
    dragRange && totalDays > 0
      ? {
          startIndex: Math.min(dragRange.start, dragRange.end),
          endIndex: Math.max(dragRange.start, dragRange.end),
        }
      : null;

  const syncDragRange = (range: { start: number; end: number } | null) => {
    dragRangeRef.current = range;
    setDragRange(range);
  };

  const openMilestoneAt = (index: number, clientX: number, clientY: number) => {
    const dueDate = format(dayDates[index], "yyyy-MM-dd");
    setPopover({ type: "milestone", dueDate, anchor: { top: clientY + 8, left: clientX - 140 } });
  };

  const finishPhaseDrag = useCallback(() => {
    const range = dragRangeRef.current;
    const track = trackRef.current;
    if (!range || !track || totalDays <= 0) {
      syncDragRange(null);
      return;
    }
    const { start_date, end_date } = dateStringsFromDayIndices(dayDates, range.start, range.end);
    const anchor = phasePopoverAnchorFromTrack(
      track.getBoundingClientRect(),
      range.start,
      range.end,
      totalDays,
    );
    syncDragRange(null);
    setPopover({ type: "phase", startDate: start_date, endDate: end_date, anchor });
  }, [dayDates, totalDays]);

  const onPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
    if (!trackRef.current || event.button !== 0 || !enablePhaseDrag) return;
    if ((event.target as HTMLElement).closest(".milestone-pin, .phase-bar")) return;
    const index = dayIndexFromPointer(event.clientX, trackRef.current.getBoundingClientRect(), totalDays);
    syncDragRange({ start: index, end: index });
    trackRef.current.setPointerCapture(event.pointerId);
    event.preventDefault();
  };

  const onPointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
    if (!dragRangeRef.current || !trackRef.current) return;
    const index = dayIndexFromPointer(event.clientX, trackRef.current.getBoundingClientRect(), totalDays);
    syncDragRange({ start: dragRangeRef.current.start, end: index });
  };

  const onPointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
    if (!trackRef.current?.hasPointerCapture(event.pointerId)) return;
    trackRef.current.releasePointerCapture(event.pointerId);
    if (enablePhaseDrag) finishPhaseDrag();
  };

  const onPointerCancel = (event: React.PointerEvent<HTMLDivElement>) => {
    if (!trackRef.current?.hasPointerCapture(event.pointerId)) return;
    trackRef.current.releasePointerCapture(event.pointerId);
    syncDragRange(null);
  };

  const phases = project.phases ?? [];
  const milestones = project.milestones ?? [];

  const trackClassName = [
    "planner-timeline",
    "planner-calendar-track",
    `planner-calendar-${mode}`,
    enablePhaseDrag ? "planner-drag-timeline" : "",
    dragRange ? "is-dragging" : "",
  ]
    .filter(Boolean)
    .join(" ");

  return (
    <>
      <div
        className={trackClassName}
        onPointerCancel={enablePhaseDrag ? onPointerCancel : undefined}
        onPointerDown={enablePhaseDrag ? onPointerDown : undefined}
        onPointerMove={enablePhaseDrag ? onPointerMove : undefined}
        onPointerUp={enablePhaseDrag ? onPointerUp : undefined}
        ref={trackRef}
        style={enablePhaseDrag ? ({ "--planner-cols": totalDays } as React.CSSProperties) : undefined}
      >
        {mode !== "project-line" ? <div className="planner-day-grid" /> : null}
        {enablePhaseDrag
          ? dayDates.map((day, index) => (
              <div
                className={`planner-day-hit${format(day, "yyyy-MM-dd") === format(new Date(), "yyyy-MM-dd") ? " is-today" : ""}`}
                key={day.toISOString()}
                style={{
                  left: `${(index / totalDays) * 100}%`,
                  width: `${(1 / totalDays) * 100}%`,
                }}
              />
            ))
          : null}
        {enableMilestones && mode === "project-line"
          ? dayDates.map((day, index) => (
              <div
                className={`planner-milestone-day${hoveredDayIndex === index ? " is-hovered" : ""}`}
                key={day.toISOString()}
                onMouseEnter={() => setHoveredDayIndex(index)}
                onMouseLeave={() => setHoveredDayIndex((prev) => (prev === index ? null : prev))}
                style={{
                  left: `${(index / totalDays) * 100}%`,
                  width: `${(1 / totalDays) * 100}%`,
                }}
              >
                <button
                  aria-label={`Add milestone on ${format(day, "d MMM")}`}
                  className="milestone-add-button"
                  onClick={(event) => {
                    event.stopPropagation();
                    const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
                    openMilestoneAt(index, rect.left + rect.width / 2, rect.bottom);
                  }}
                  type="button"
                >
                  +
                </button>
              </div>
            ))
          : null}
        {showScheduleSpan &&
        lineSegment &&
        mode === "project-line" &&
        (!project.is_tentative || showTentative) ? (
          <div
            className={`project-span-line${project.is_tentative ? " tentative" : ""}`}
            style={{
              ...projectBarStyle(
                {
                  startIndex: lineSegment.startIndex,
                  endIndex: lineSegment.endIndex,
                  className: "project-span-line",
                  label: "",
                },
                totalDays,
              ),
              background: project.color,
            }}
            title={project.name}
          />
        ) : null}

        {mode === "phases"
          ? phases.map((phase) => (
              <PhaseBar key={phase.id} dayDates={dayDates} phase={phase} totalDays={totalDays} />
            ))
          : null}

        {previewSegment && enablePhaseDrag ? (
          <div
            className="phase-bar preview"
            style={{
              ...projectBarStyle(
                {
                  startIndex: previewSegment.startIndex,
                  endIndex: previewSegment.endIndex,
                  className: "phase-bar",
                  label: "Phase",
                },
                totalDays,
              ),
              background: phasePreviewColor,
            }}
          >
            Phase
          </div>
        ) : null}

        {enableMilestones && mode === "project-line"
          ? milestones.map((milestone) => (
              <MilestonePin dayDates={dayDates} key={milestone.id} milestone={milestone} totalDays={totalDays} />
            ))
          : null}
      </div>

      {popover?.type === "milestone"
        ? createPortal(
            <MilestoneAddPopover
              anchor={popover.anchor}
              dueDate={popover.dueDate}
              isSaving={isSaving}
              onCancel={() => setPopover(null)}
              onCreate={(name, note) => {
                onCreateMilestone(popover.dueDate, name, note);
                setPopover(null);
              }}
            />,
            document.body,
          )
        : null}

      {popover?.type === "phase"
        ? createPortal(
            <PhaseCreatePopover
              anchor={popover.anchor}
              endDate={popover.endDate}
              isSaving={isSaving}
              onCancel={() => setPopover(null)}
              onSave={(input) => {
                onCreatePhase(input);
                setPopover(null);
              }}
              startDate={popover.startDate}
            />,
            document.body,
          )
        : null}
    </>
  );
}

function PhaseBar({
  phase,
  dayDates,
  totalDays,
}: {
  phase: ProjectPhase;
  dayDates: Date[];
  totalDays: number;
}) {
  const segment = buildAllocationBarSegment(dayDates, phase.start_date, phase.end_date);
  if (!segment) return null;
  return (
    <div
      className="phase-bar"
      onPointerDown={(event) => event.stopPropagation()}
      style={{
        ...projectBarStyle(segment, totalDays),
        background: phase.color,
      }}
      title={phase.name}
    >
      {phase.name}
    </div>
  );
}

function MilestonePin({
  milestone,
  dayDates,
  totalDays,
}: {
  milestone: Milestone;
  dayDates: Date[];
  totalDays: number;
}) {
  const index = dayDates.findIndex((day) => format(day, "yyyy-MM-dd") === milestone.due_date);
  if (index < 0) return null;
  const left = ((index + 0.5) / totalDays) * 100;
  return (
    <span
      className={`milestone-pin${milestone.is_completed ? " completed" : ""}`}
      style={{ left: `${left}%` }}
      title={`${milestone.name} · ${milestone.due_date}`}
    />
  );
}
