"use client";

import { useMemo, useRef, useState } from "react";
import type { MouseEvent as ReactMouseEvent, WheelEvent as ReactWheelEvent } from "react";
import { ChevronDown, ChevronLeft, ChevronRight, EyeOff, Minus, Plus } from "lucide-react";
import { Badge, type BadgeTone } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { PoleBadge } from "@/components/pole-badge";
import { useProjectStates } from "@/components/use-project-states";
import { type ProjectState } from "@/lib/project-state";
import { cn } from "@/lib/utils";

type SortKey = "currentMonth" | "client" | "pole" | "project";
type DetailMode = "planning" | "collaborators";
type ResizableColumnKey = "client" | "project" | "pole" | "risk";

type Project = {
  id: string;
  name: string;
  client: string;
  manager: string;
  budget: string;
  progress: number;
  temperature: string;
  tone: BadgeTone;
  riskLabel?: string;
  riskTone?: BadgeTone;
  poles: string[];
  nextMilestone: string;
  nextMilestoneDate: string;
  startsAt?: string;
  endsAt?: string;
};

type Phase = {
  name: string;
  from: string;
  to: string;
  tasks: string[];
  milestone: string;
  startDate?: string;
  endDate?: string;
  milestoneDate?: string;
};

type PlanningDetail = {
  projectId: string;
  status: string;
  rest: string;
  weeks: number[];
  team: Array<{ name: string; role: string; weekly: number[] }>;
  phases: Phase[];
};

const allWeeks = Array.from({ length: 52 }, (_, index) => `S${index + 1}`);
const monthNames = [
  "Janvier",
  "Février",
  "Mars",
  "Avril",
  "Mai",
  "Juin",
  "Juillet",
  "Août",
  "Septembre",
  "Octobre",
  "Novembre",
  "Décembre"
];
const currentMonthStart = new Date(2026, 7, 1);
const currentMonthEnd = new Date(2026, 7, 31, 23, 59, 59, 999);
const wheelPixelsPerWeek = 60;
const columnMinWidths: Record<ResizableColumnKey, number> = {
  client: 96,
  project: 160,
  pole: 72,
  risk: 72
};
const columnMaxWidths: Record<ResizableColumnKey, number> = {
  client: 260,
  project: 420,
  pole: 180,
  risk: 180
};

export function ProjectPlanningBoard({ projects, planning }: { projects: Project[]; planning: PlanningDetail[] }) {
  const [expandedId, setExpandedId] = useState(projects[0]?.id ?? "");
  const [startWeek, setStartWeek] = useState(0);
  const [visibleCount, setVisibleCount] = useState(20);
  const [sortKey, setSortKey] = useState<SortKey>("currentMonth");
  const [showFinished, setShowFinished] = useState(false);
  const [detailMode, setDetailMode] = useState<DetailMode>("planning");
  const [columnWidths, setColumnWidths] = useState({
    client: 150,
    project: 220,
    pole: 112,
    risk: 104
  });
  const wheelRemainder = useRef(0);
  const { statesByProject } = useProjectStates(projects);
  const planningByProject = useMemo(() => new Map(planning.map((item) => [item.projectId, item])), [planning]);
  const sortedProjects = useMemo(() => {
    return [...projects]
      .filter((project) => showFinished || statesByProject[project.id] !== "Fini")
      .sort((a, b) => {
        if (sortKey === "currentMonth") {
          const stateDelta = getStateSortRank(statesByProject[a.id]) - getStateSortRank(statesByProject[b.id]);

          if (stateDelta !== 0) {
            return stateDelta;
          }

          const activeDelta = Number(isActiveInCurrentMonth(b)) - Number(isActiveInCurrentMonth(a));

          if (activeDelta !== 0) {
            return activeDelta;
          }
        }

        if (sortKey === "pole") {
          return (a.poles[0] ?? "").localeCompare(b.poles[0] ?? "", "fr");
        }

        if (sortKey === "project") {
          return a.name.localeCompare(b.name, "fr");
        }

        return a.client.localeCompare(b.client, "fr");
      });
  }, [projects, showFinished, sortKey, statesByProject]);
  const visibleWeeks = allWeeks.slice(startWeek, startWeek + visibleCount);
  const monthBands = buildMonthBands(startWeek, visibleCount);
  const metaGrid = `${columnWidths.client}px ${columnWidths.project}px ${columnWidths.pole}px ${columnWidths.risk}px 64px`;

  function scrollWeeks(delta: number) {
    setStartWeek((value) => Math.max(0, Math.min(allWeeks.length - visibleCount, value + delta)));
  }

  function handleTimelineWheel(event: ReactWheelEvent<HTMLDivElement>) {
    const rawDelta = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
    const delta = rawDelta * getWheelModeMultiplier(event.deltaMode);

    if (Math.abs(delta) < 2) {
      return;
    }

    event.preventDefault();
    event.stopPropagation();
    event.nativeEvent.stopImmediatePropagation();
    wheelRemainder.current += delta;

    const weekDelta = Math.trunc(wheelRemainder.current / wheelPixelsPerWeek);

    if (weekDelta !== 0) {
      scrollWeeks(weekDelta);
      wheelRemainder.current -= weekDelta * wheelPixelsPerWeek;
    }
  }

  function startColumnResize(column: ResizableColumnKey, event: ReactMouseEvent<HTMLButtonElement>) {
    event.preventDefault();
    event.stopPropagation();

    const startX = event.clientX;
    const startWidth = columnWidths[column];

    function handleMove(moveEvent: MouseEvent) {
      const nextWidth = Math.min(columnMaxWidths[column], Math.max(columnMinWidths[column], startWidth + moveEvent.clientX - startX));
      setColumnWidths((current) => ({ ...current, [column]: nextWidth }));
    }

    function handleUp() {
      window.removeEventListener("mousemove", handleMove);
      window.removeEventListener("mouseup", handleUp);
    }

    window.addEventListener("mousemove", handleMove);
    window.addEventListener("mouseup", handleUp);
  }

  return (
    <section className="w-full px-3 py-4 lg:px-5">
      <div className="overflow-hidden rounded-lg border border-border bg-white">
        <div className="flex flex-col gap-3 border-b border-border p-3 xl:flex-row xl:items-center xl:justify-between">
          <div>
            <h2 className="text-base font-semibold text-primary">Planning projets</h2>
            <p className="mt-1 text-sm text-muted-foreground">Lecture seule : les modifications seront faites dans une page dédiée.</p>
          </div>
          <div className="flex flex-wrap gap-2">
            <Badge tone="blue">{sortedProjects.length} projets</Badge>
            <SelectControl label="Tri" value={sortKey} onChange={(value) => setSortKey(value as SortKey)}>
              <option value="currentMonth">Actifs ce mois</option>
              <option value="client">Client A-Z</option>
              <option value="pole">Pôle A-Z</option>
              <option value="project">Projet A-Z</option>
            </SelectControl>
            <Button type="button" variant={showFinished ? "primary" : "secondary"} onClick={() => setShowFinished((value) => !value)}>
              <EyeOff className="h-4 w-4" />
              {showFinished ? "Tous les projets" : "Masquer finis"}
            </Button>
            <Button type="button" variant={detailMode === "planning" ? "primary" : "secondary"} onClick={() => setDetailMode("planning")}>
              Planning
            </Button>
            <Button type="button" variant={detailMode === "collaborators" ? "primary" : "secondary"} onClick={() => setDetailMode("collaborators")}>
              Collaborateurs
            </Button>
            <IconButton label="Dézoomer" onClick={() => setVisibleCount((value) => Math.min(28, value + 4))}>
              <Minus className="h-4 w-4" />
            </IconButton>
            <IconButton label="Zoomer" onClick={() => setVisibleCount((value) => Math.max(8, value - 4))}>
              <Plus className="h-4 w-4" />
            </IconButton>
            <IconButton label="Période précédente" onClick={() => setStartWeek((value) => Math.max(0, value - 4))}>
              <ChevronLeft className="h-4 w-4" />
            </IconButton>
            <IconButton label="Période suivante" onClick={() => setStartWeek((value) => Math.min(allWeeks.length - visibleCount, value + 4))}>
              <ChevronRight className="h-4 w-4" />
            </IconButton>
          </div>
        </div>

        <div className="overflow-x-auto overscroll-x-contain">
          <div className="min-w-[1320px]">
            <PlanningHeader
              metaGrid={metaGrid}
              monthBands={monthBands}
              visibleWeeks={visibleWeeks}
              onColumnResizeStart={startColumnResize}
              onTimelineWheel={handleTimelineWheel}
            />
            {sortedProjects.map((project, index) => {
              const detail = planningByProject.get(project.id) ?? createFallbackPlanning(project, index);
              const isExpanded = expandedId === project.id;
              const range = getPlanningRange(detail);

              return (
                <div key={project.id} className="border-b border-border">
                  <button
                    type="button"
                    className={cn("grid w-full text-left text-xs hover:bg-[#fff7fa]", isExpanded ? "bg-[#fff7fa]" : "bg-white")}
                    style={{ gridTemplateColumns: `${metaGrid} minmax(720px, 1fr)` }}
                    onClick={() => setExpandedId(isExpanded ? "" : project.id)}
                  >
                    <MetaCells project={project} detail={detail} isExpanded={isExpanded} />
                    <TimelineCell
                      bars={[{ from: range.from, to: range.to, label: project.name, tone: "project" }]}
                      milestones={getMilestones(detail.phases)}
                      startWeek={startWeek}
                      visibleCount={visibleCount}
                      heightClass="h-11"
                      onTimelineWheel={handleTimelineWheel}
                    />
                  </button>

                  {isExpanded && detailMode === "planning" ? (
                    <div>
                      {detail.phases.map((phase, phaseIndex) => (
                        <div
                          key={`${project.id}-${phase.name}`}
                          className="grid bg-[#fbf8fb] text-xs"
                          style={{ gridTemplateColumns: `${metaGrid} minmax(720px, 1fr)` }}
                        >
                          <div className="border-r border-border px-8 py-1.5 font-black text-primary">{getPhaseNumberLabel(phase, phaseIndex)}</div>
                          <div className="truncate border-r border-border px-2 py-1.5 font-medium text-primary" title={getPhaseDescription(phase)}>
                            {getPhaseDescription(phase)}
                          </div>
                          <div className="border-r border-border" />
                          <div className="border-r border-border" />
                          <div />
                          <TimelineCell
                            bars={[{ from: getPhaseStartIndex(phase), to: getPhaseEndIndex(phase), label: phase.name, tone: "phase" }]}
                            milestones={[{ week: getPhaseMilestoneIndex(phase), label: phase.milestone }]}
                            startWeek={startWeek}
                            visibleCount={visibleCount}
                            heightClass="h-7"
                            onTimelineWheel={handleTimelineWheel}
                          />
                        </div>
                      ))}
                    </div>
                  ) : null}
                  {isExpanded && detailMode === "collaborators" ? (
                    <CollaboratorRows
                      detail={detail}
                      metaGrid={metaGrid}
                      startWeek={startWeek}
                      visibleCount={visibleCount}
                      onTimelineWheel={handleTimelineWheel}
                    />
                  ) : null}
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </section>
  );
}

function CollaboratorRows({
  detail,
  metaGrid,
  startWeek,
  visibleCount,
  onTimelineWheel
}: {
  detail: PlanningDetail;
  metaGrid: string;
  startWeek: number;
  visibleCount: number;
  onTimelineWheel: (event: ReactWheelEvent<HTMLDivElement>) => void;
}) {
  return (
    <div>
      {detail.team.map((member) => {
        const visibleValues = member.weekly.slice(startWeek, startWeek + visibleCount);
        const total = visibleValues.reduce((sum, value) => sum + value, 0);
        const range = getLoadRange(member.weekly, startWeek, visibleCount);

        return (
          <div
            key={`${detail.projectId}-${member.name}`}
            className="grid bg-[#fbf8fb] text-xs"
            style={{ gridTemplateColumns: `${metaGrid} minmax(720px, 1fr)` }}
          >
            <div className="border-r border-border px-8 py-1.5 font-black text-primary">{member.name}</div>
            <div className="truncate border-r border-border px-2 py-1.5 font-medium text-primary">{member.role || "Projet"}</div>
            <div />
            <div />
            <div className="flex items-center justify-end border-r border-border px-1.5 py-1.5">
              <span className="rounded-sm bg-[#eee9f6] px-1.5 py-1 text-xs font-black tabular-nums text-primary">
                {formatDays(total)}
              </span>
            </div>
            <TimelineCell
              bars={range ? [{ from: range.from, to: range.to, label: member.name, tone: "phase" }] : []}
              milestones={[]}
              loads={member.weekly}
              startWeek={startWeek}
              visibleCount={visibleCount}
              heightClass="h-8"
              onTimelineWheel={onTimelineWheel}
            />
          </div>
        );
      })}
    </div>
  );
}

function MetaCells({ project, detail, isExpanded }: { project: Project; detail: PlanningDetail; isExpanded: boolean }) {
  return (
    <>
      <div className="flex min-w-0 items-center gap-1.5 border-r border-border px-2 py-2">
        <ChevronDown className={cn("h-3.5 w-3.5 shrink-0 text-primary transition-transform", !isExpanded && "-rotate-90")} />
        <span className="truncate font-black uppercase tracking-normal text-primary" title={project.client}>
          {project.client}
        </span>
      </div>
      <div className="min-w-0 border-r border-border px-2 py-2">
        <span className="block truncate font-bold text-primary" title={project.name}>
          {project.name}
        </span>
      </div>
      <div className="flex flex-wrap items-center gap-1 border-r border-border px-2 py-2">
        {project.poles.slice(0, 1).map((pole) => (
          <PoleBadge key={pole} pole={pole} compact className="h-5 px-1.5 text-[10px]" />
        ))}
      </div>
      <div className="border-r border-border px-2 py-2">
        <Badge tone={project.riskTone ?? project.tone} className="h-5 max-w-full truncate px-1.5 text-[10px]">
          {compactRisk(project.riskLabel ?? project.temperature)}
        </Badge>
      </div>
      <div className="flex items-center justify-end border-r border-border px-1.5 py-2">
        <span
          className={cn(
            "rounded-sm px-1.5 py-1 text-xs font-black tabular-nums",
            detail.rest.startsWith("-") ? "bg-accent text-white" : "bg-[#eee9f6] text-primary"
          )}
        >
          {detail.rest}
        </span>
      </div>
    </>
  );
}

function PlanningHeader({
  metaGrid,
  monthBands,
  visibleWeeks,
  onColumnResizeStart,
  onTimelineWheel
}: {
  metaGrid: string;
  monthBands: Array<{ label: string; span: number }>;
  visibleWeeks: string[];
  onColumnResizeStart: (column: ResizableColumnKey, event: ReactMouseEvent<HTMLButtonElement>) => void;
  onTimelineWheel: (event: ReactWheelEvent<HTMLDivElement>) => void;
}) {
  return (
    <>
      <div className="grid bg-primary text-[11px] font-black uppercase text-white" style={{ gridTemplateColumns: `${metaGrid} minmax(720px, 1fr)` }}>
        <ResizableHeaderCell label="Client" column="client" onColumnResizeStart={onColumnResizeStart} />
        <ResizableHeaderCell label="Projet" column="project" onColumnResizeStart={onColumnResizeStart} />
        <ResizableHeaderCell label="Pôle" column="pole" onColumnResizeStart={onColumnResizeStart} />
        <ResizableHeaderCell label="Risque" column="risk" onColumnResizeStart={onColumnResizeStart} />
        <div className="border-r border-white/15 px-2 py-2.5 text-right">Reste</div>
        <div
          className="grid cursor-ew-resize"
          onWheel={onTimelineWheel}
          style={{ gridTemplateColumns: `repeat(${visibleWeeks.length}, minmax(32px, 1fr))` }}
          title="Glisser au trackpad pour changer de période"
        >
          {monthBands.map((month) => (
            <div key={month.label} className="border-r border-white/15 px-2 py-3 text-center" style={{ gridColumn: `span ${month.span}` }}>
              {month.label}
            </div>
          ))}
        </div>
      </div>
      <div className="grid border-b border-border bg-[#fff0f4] text-[11px] font-bold text-primary" style={{ gridTemplateColumns: `${metaGrid} minmax(720px, 1fr)` }}>
        <div className="border-r border-border" />
        <div className="border-r border-border" />
        <div className="border-r border-border" />
        <div className="border-r border-border" />
        <div className="border-r border-border" />
        <div
          className="grid cursor-ew-resize"
          onWheel={onTimelineWheel}
          style={{ gridTemplateColumns: `repeat(${visibleWeeks.length}, minmax(32px, 1fr))` }}
          title="Glisser au trackpad pour changer de période"
        >
          {visibleWeeks.map((week) => (
            <div key={week} className="border-r border-[#f2b5c7] px-1 py-2 text-center">
              {week}
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

function ResizableHeaderCell({
  label,
  column,
  onColumnResizeStart
}: {
  label: string;
  column: ResizableColumnKey;
  onColumnResizeStart: (column: ResizableColumnKey, event: ReactMouseEvent<HTMLButtonElement>) => void;
}) {
  return (
    <div className="relative border-r border-white/15 px-2 py-2.5">
      {label}
      <button
        type="button"
        aria-label={`Modifier la largeur ${label}`}
        className="absolute right-0 top-0 h-full w-2 cursor-col-resize border-0 bg-transparent p-0 hover:bg-white/20"
        onMouseDown={(event) => onColumnResizeStart(column, event)}
      />
    </div>
  );
}

function TimelineCell({
  bars,
  milestones,
  loads,
  startWeek,
  visibleCount,
  heightClass,
  onTimelineWheel
}: {
  bars: Array<{ from: number; to: number; label: string; tone: "project" | "phase" }>;
  milestones: Array<{ week: number; label: string }>;
  loads?: number[];
  startWeek: number;
  visibleCount: number;
  heightClass: string;
  onTimelineWheel?: (event: ReactWheelEvent<HTMLDivElement>) => void;
}) {
  return (
    <div className={cn("relative cursor-ew-resize border-l border-border", heightClass)} onWheel={onTimelineWheel}>
      <div className="absolute inset-0 grid" style={{ gridTemplateColumns: `repeat(${visibleCount}, minmax(32px, 1fr))` }}>
        {Array.from({ length: visibleCount }, (_, index) => (
          <div key={index} className="border-r border-[#ebe6f1]" />
        ))}
      </div>
      {bars.map((bar) => (
        <Bar key={`${bar.label}-${bar.from}-${bar.to}`} {...bar} startWeek={startWeek} visibleCount={visibleCount} />
      ))}
      {loads
        ? loads.slice(startWeek, startWeek + visibleCount).map((value, index) =>
            value > 0 ? (
              <span
                key={`${startWeek + index}-${value}`}
                className="absolute top-1/2 z-20 -translate-x-1/2 -translate-y-1/2 rounded-sm bg-primary px-1 text-[10px] font-bold text-white"
                style={{ left: `${((index + 0.5) / visibleCount) * 100}%` }}
              >
                {formatNumber(value)}
              </span>
            ) : null
          )
        : null}
      {milestones.map((milestone) => (
        <Milestone key={`${milestone.label}-${milestone.week}`} {...milestone} startWeek={startWeek} visibleCount={visibleCount} />
      ))}
    </div>
  );
}

function Bar({
  from,
  to,
  tone,
  startWeek,
  visibleCount
}: {
  from: number;
  to: number;
  tone: "project" | "phase";
  startWeek: number;
  visibleCount: number;
}) {
  const leftWeek = Math.max(from, startWeek);
  const rightWeek = Math.min(to, startWeek + visibleCount - 1);

  if (rightWeek < startWeek || leftWeek > startWeek + visibleCount - 1) {
    return null;
  }

  const left = ((leftWeek - startWeek) / visibleCount) * 100;
  const width = ((rightWeek - leftWeek + 1) / visibleCount) * 100;

  return (
    <div
      className={cn(
        "absolute top-1/2 z-10 h-5 -translate-y-1/2 rounded-full border bg-white",
        tone === "project" ? "border-primary" : "border-[#77708e]"
      )}
      style={{ left: `${left}%`, width: `${width}%` }}
    />
  );
}

function Milestone({
  week,
  label,
  startWeek,
  visibleCount
}: {
  week: number;
  label: string;
  startWeek: number;
  visibleCount: number;
}) {
  if (week < startWeek || week > startWeek + visibleCount - 1) {
    return null;
  }

  const left = ((week - startWeek + 0.5) / visibleCount) * 100;

  return (
    <span
      title={label}
      className="absolute top-1/2 z-20 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white bg-primary shadow-sm"
      style={{ left: `${left}%` }}
    />
  );
}

function IconButton({ label, onClick, children }: { label: string; onClick: () => void; children: React.ReactNode }) {
  return (
    <Button type="button" variant="secondary" className="h-9 w-9 px-0" onClick={onClick} title={label}>
      {children}
    </Button>
  );
}

function SelectControl({
  label,
  value,
  onChange,
  children
}: {
  label: string;
  value: string;
  onChange: (value: string) => void;
  children: React.ReactNode;
}) {
  return (
    <label className="flex h-9 items-center gap-2 rounded-md border border-border bg-white px-2 text-xs font-semibold text-primary">
      {label}
      <select className="bg-transparent text-xs font-semibold outline-none" value={value} onChange={(event) => onChange(event.target.value)}>
        {children}
      </select>
    </label>
  );
}

function createFallbackPlanning(project: Project, index: number): PlanningDetail {
  const start = index % 8;
  const length = 4 + (index % 6);
  const weeksData = allWeeks.map((_, weekIndex) => {
    if (weekIndex < start || weekIndex > start + length) {
      return 0;
    }

    return ((weekIndex + index) % 3) + 1;
  });

  return {
    projectId: project.id,
    status: "En cours",
    rest: index % 5 === 0 ? "-2 j" : `${Math.max(1, 12 - index)} j`,
    weeks: weeksData,
    team: [{ name: project.manager, role: "CP", weekly: weeksData }],
    phases: [
      {
        name: "Phase active",
        from: allWeeks[start] ?? "S1",
        to: allWeeks[Math.min(start + length, allWeeks.length - 1)] ?? "S16",
        tasks: ["Cadrage", "Production", "Restitution"],
        milestone: project.nextMilestone
      }
    ]
  };
}

function getPlanningRange(detail: PlanningDetail) {
  const activeWeeks = detail.weeks.map((value, index) => (value > 0 ? index : -1)).filter((index) => index >= 0);
  const phaseWeeks = detail.phases.flatMap((phase) => [getPhaseStartIndex(phase), getPhaseEndIndex(phase)]);
  const hasDetailedPhases = detail.phases.some((phase) => phase.name !== "Projet" || phase.startDate || phase.endDate);
  const weeks = [...(hasDetailedPhases ? [] : activeWeeks), ...phaseWeeks].filter((week) => Number.isFinite(week));

  return {
    from: Math.min(...(weeks.length ? weeks : [0])),
    to: Math.max(...(weeks.length ? weeks : [4]))
  };
}

function getLoadRange(values: number[], startWeek: number, visibleCount: number) {
  const activeWeeks = values
    .map((value, index) => (value > 0 ? index : -1))
    .filter((index) => index >= startWeek && index < startWeek + visibleCount);

  if (activeWeeks.length === 0) {
    return undefined;
  }

  return {
    from: Math.min(...activeWeeks),
    to: Math.max(...activeWeeks)
  };
}

function getMilestones(phases: Phase[]) {
  return phases.map((phase) => ({ week: getPhaseMilestoneIndex(phase), label: phase.milestone }));
}

function getWheelModeMultiplier(deltaMode: number) {
  if (deltaMode === 1) {
    return 24;
  }

  if (deltaMode === 2) {
    return 240;
  }

  return 1;
}

function getPhaseNumberLabel(phase: Phase, index: number) {
  const code = phase.name.match(/^([A-Z]|\d+)\b/)?.[1];

  if (/^\d+$/.test(code ?? "")) {
    return `Phase ${code}`;
  }

  return `Phase ${index + 1}`;
}

function getPhaseDescription(phase: Phase) {
  return phase.name.replace(/^([A-Z]|\d+)\s+/, "");
}

function phaseToIndex(week: string) {
  const dateIndex = dateToWeekIndex(week);

  if (typeof dateIndex === "number") {
    return dateIndex;
  }

  return Math.max(0, Number(week.replace("S", "")) - 1);
}

function getPhaseStartIndex(phase: Phase) {
  return dateToWeekIndex(phase.startDate) ?? phaseToIndex(phase.from);
}

function getPhaseEndIndex(phase: Phase) {
  return dateToWeekIndex(phase.endDate) ?? phaseToIndex(phase.to);
}

function getPhaseMilestoneIndex(phase: Phase) {
  return dateToWeekIndex(phase.milestoneDate) ?? getPhaseEndIndex(phase);
}

function dateToWeekIndex(value?: string) {
  if (!value) {
    return undefined;
  }

  const date = parseProjectDate(value);

  if (!date) {
    return undefined;
  }

  return Math.max(0, Math.min(51, getIsoWeek(date) - 1));
}

function compactRisk(label: string) {
  const compactLabels: Record<string, string> = {
    "OK planning": "OK",
    "A surveiller": "Surv.",
    "Risque charge": "Charge",
    "Risque budget": "Budget",
    "Risque planning": "Planning",
    "A facturer": "Fact.",
    "A archiver": "Fini",
    "Budget à compléter": "Budget"
  };

  return compactLabels[label] ?? label;
}

function formatNumber(value: number) {
  return value.toLocaleString("fr-FR", { maximumFractionDigits: 1 });
}

function formatDays(value: number) {
  return `${formatNumber(value)} j`;
}

function buildMonthBands(startWeek: number, visibleCount: number) {
  const bands: Array<{ label: string; span: number }> = [];

  for (let index = 0; index < visibleCount; index += 1) {
    const month = monthNames[Math.floor((startWeek + index) / 4.33)] ?? "Suite";
    const current = bands[bands.length - 1];

    if (current?.label === month) {
      current.span += 1;
    } else {
      bands.push({ label: month, span: 1 });
    }
  }

  return bands;
}

function isActiveInCurrentMonth(project: Project) {
  const startsAt = parseProjectDate(project.startsAt);
  const endsAt = parseProjectDate(project.endsAt);

  if (!startsAt && !endsAt) {
    return false;
  }

  return (!startsAt || startsAt <= currentMonthEnd) && (!endsAt || endsAt >= currentMonthStart);
}

function parseProjectDate(value?: string) {
  if (!value) {
    return undefined;
  }

  const date = new Date(value);

  return Number.isNaN(date.getTime()) ? undefined : date;
}

function getIsoWeek(date: Date) {
  const copy = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
  const day = copy.getUTCDay() || 7;
  copy.setUTCDate(copy.getUTCDate() + 4 - day);
  const yearStart = new Date(Date.UTC(copy.getUTCFullYear(), 0, 1));
  return Math.ceil(((copy.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
}

function getStateSortRank(state: ProjectState) {
  const ranks: Record<ProjectState, number> = {
    Actif: 0,
    "À venir": 1,
    "En standby": 2,
    Fini: 3
  };

  return ranks[state];
}
