import { addWeeks, format, parseISO } from "date-fns";
import { Calendar, ChevronRight, HelpCircle, MoreHorizontal } from "lucide-react";
import { useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";

import {
  allocationPctFromEffort,
  effortValueFromPct,
  endDateFromWeekdayCount,
  formatHoursClock,
  parseHoursClock,
  totalEffortDays,
  totalEffortHours,
  type AllocationRepeatConfig,
  type EffortUnit,
  type TotalEffortUnit,
} from "../../planner/allocationRepeat";
import {
  hoursPerDayFromAllocation,
  roundToHalfHour,
  visibleWeekdayCount,
} from "../../planner/allocationTimeline";
import type { Allocation, Person } from "../../types";

export interface AllocationDragDraft {
  person_id: number;
  start_date: string;
  end_date: string;
  allocation_pct: number;
  hours_per_day: number;
  work_days: number;
  note?: string;
  repeat?: AllocationRepeatConfig;
}

interface AllocationCreatePopoverProps {
  anchor: { top: number; left: number };
  dayDates: Date[];
  startDate: string;
  endDate: string;
  people: Person[];
  fixedPerson?: Person;
  defaultHoursPerDay?: number;
  allocation?: Allocation;
  onCancel: () => void;
  onSave: (draft: AllocationDragDraft) => void;
  onUpdate?: (draft: AllocationDragDraft) => void;
  onDelete?: () => void;
  transferPeople?: Person[];
  onTransfer?: (personId: number) => void;
  onClone?: () => void;
  onSelectAllToRight?: () => void;
  onToggleMultiSelect?: () => void;
  multiSelectActive?: boolean;
  isSaving?: boolean;
}

const EFFORT_UNITS: Array<{ value: EffortUnit; label: string }> = [
  { value: "hours_per_day", label: "h/d" },
  { value: "hours_per_week", label: "h/wk" },
  { value: "capacity_pct", label: "%" },
  { value: "fte", label: "FTE" },
];

export function AllocationCreatePopover({
  anchor,
  dayDates,
  startDate,
  endDate,
  people,
  fixedPerson,
  defaultHoursPerDay,
  allocation,
  onCancel,
  onSave,
  onUpdate,
  onDelete,
  transferPeople = [],
  onTransfer,
  onClone,
  onSelectAllToRight,
  onToggleMultiSelect,
  multiSelectActive = false,
  isSaving = false,
}: AllocationCreatePopoverProps) {
  const popoverRef = useRef<HTMLDivElement>(null);
  const menuRef = useRef<HTMLDivElement>(null);
  const isEdit = Boolean(allocation);
  const dragWorkDays = visibleWeekdayCount(dayDates, startDate, endDate);

  const [personId, setPersonId] = useState<number | "">(fixedPerson?.id ?? people[0]?.id ?? "");
  const [rangeStart, setRangeStart] = useState(startDate);
  const [rangeEnd, setRangeEnd] = useState(endDate);
  const [workDays, setWorkDays] = useState(Math.max(1, dragWorkDays));
  const [effortUnit, setEffortUnit] = useState<EffortUnit>("hours_per_day");
  const [effortInput, setEffortInput] = useState("8:00");
  const [totalUnit, setTotalUnit] = useState<TotalEffortUnit>("hours");
  const [repeatEnabled, setRepeatEnabled] = useState(false);
  const [repeatEndMode, setRepeatEndMode] = useState<"on" | "after">("on");
  const [repeatEndOn, setRepeatEndOn] = useState(format(addWeeks(parseISO(startDate), 4), "yyyy-MM-dd"));
  const [repeatOccurrences, setRepeatOccurrences] = useState(4);
  const [noteOpen, setNoteOpen] = useState(false);
  const [note, setNote] = useState("");
  const [datesOpen, setDatesOpen] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  const [transferOpen, setTransferOpen] = useState(false);

  const selectedPerson =
    fixedPerson ?? people.find((person) => person.id === Number(personId));
  const capacityDaily = selectedPerson ? roundToHalfHour(selectedPerson.weekly_capacity_hrs / 5) : 8;

  useEffect(() => {
    const onKey = (event: KeyboardEvent) => {
      if (event.key === "Escape") onCancel();
    };
    const onDown = (event: MouseEvent) => {
      const target = event.target as Node;
      if (popoverRef.current?.contains(target)) return;
      if (menuRef.current?.contains(target)) return;
      onCancel();
    };
    window.addEventListener("keydown", onKey);
    document.addEventListener("mousedown", onDown);
    return () => {
      window.removeEventListener("keydown", onKey);
      document.removeEventListener("mousedown", onDown);
    };
  }, [onCancel]);

  useEffect(() => {
    setPersonId(fixedPerson?.id ?? people[0]?.id ?? "");
    setDatesOpen(false);
    setMenuOpen(false);
    setTransferOpen(false);

    const editPerson = allocation
      ? (fixedPerson ?? people.find((person) => person.id === allocation.person_id))
      : undefined;

    if (allocation && editPerson) {
      const savedWorkDays = visibleWeekdayCount(
        dayDates,
        allocation.start_date,
        allocation.end_date,
      );
      const savedHoursPerDay = hoursPerDayFromAllocation(
        allocation.allocation_pct,
        editPerson.weekly_capacity_hrs,
      );
      setRangeStart(allocation.start_date);
      setRangeEnd(allocation.end_date);
      setWorkDays(Math.max(1, savedWorkDays));
      setRepeatEnabled(false);
      setNote(allocation.note ?? "");
      setNoteOpen(Boolean(allocation.note));
      setEffortUnit("hours_per_day");
      setEffortInput(formatHoursClock(savedHoursPerDay));
      return;
    }

    setRangeStart(startDate);
    setRangeEnd(endDate);
    setWorkDays(Math.max(1, dragWorkDays));
    setRepeatEndOn(format(addWeeks(parseISO(startDate), 4), "yyyy-MM-dd"));
    setRepeatOccurrences(4);
    setRepeatEnabled(false);
    setNote("");
    setNoteOpen(false);
    setEffortUnit("hours_per_day");
    setEffortInput(formatHoursClock(defaultHoursPerDay ?? capacityDaily));
  }, [
    allocation,
    fixedPerson,
    people,
    defaultHoursPerDay,
    capacityDaily,
    startDate,
    endDate,
    dragWorkDays,
    dayDates,
  ]);

  useEffect(() => {
    setRangeEnd(endDateFromWeekdayCount(rangeStart, workDays));
  }, [rangeStart, workDays]);

  const effortValue = useMemo(() => {
    if (effortUnit === "capacity_pct" || effortUnit === "fte") {
      return Number(effortInput) || 0;
    }
    return parseHoursClock(effortInput);
  }, [effortInput, effortUnit]);

  const allocationPct = selectedPerson
    ? allocationPctFromEffort(effortUnit, effortValue, selectedPerson.weekly_capacity_hrs)
    : 50;

  const hoursPerDay = selectedPerson
    ? roundToHalfHour((allocationPct / 100) * (selectedPerson.weekly_capacity_hrs / 5))
    : roundToHalfHour(effortValue);

  const totalHours = totalEffortHours(hoursPerDay, workDays);
  const totalDays = selectedPerson
    ? totalEffortDays(hoursPerDay, workDays, selectedPerson.weekly_capacity_hrs)
    : workDays;

  const handleEffortUnitChange = (unit: EffortUnit) => {
    if (!selectedPerson) {
      setEffortUnit(unit);
      return;
    }
    const nextValue = effortValueFromPct(unit, allocationPct, selectedPerson.weekly_capacity_hrs);
    setEffortUnit(unit);
    if (unit === "capacity_pct") setEffortInput(String(Math.round(nextValue)));
    else if (unit === "fte") setEffortInput(String(roundToHalfHour(nextValue * 10) / 10));
    else setEffortInput(formatHoursClock(nextValue));
  };

  const handlePersonChange = (nextPersonId: number) => {
    setPersonId(nextPersonId);
    const person = people.find((item) => item.id === nextPersonId);
    if (!person) return;
    setEffortUnit("hours_per_day");
    setEffortInput(formatHoursClock(roundToHalfHour(person.weekly_capacity_hrs / 5)));
  };

  const buildDraft = (): AllocationDragDraft => ({
    person_id: selectedPerson!.id,
    start_date: rangeStart,
    end_date: rangeEnd,
    allocation_pct: allocationPct,
    hours_per_day: hoursPerDay,
    work_days: workDays,
    note: note.trim() || undefined,
    repeat: {
      enabled: !isEdit && repeatEnabled,
      interval: "week",
      endMode: repeatEndMode,
      endOn: repeatEndOn,
      occurrences: Math.max(1, repeatOccurrences),
    },
  });

  const handleSave = () => {
    if (!selectedPerson || personId === "") return;
    const draft = buildDraft();
    if (isEdit && onUpdate) {
      onUpdate(draft);
      return;
    }
    onSave(draft);
  };

  const style = {
    top: Math.min(anchor.top, window.innerHeight - 320),
    left: Math.min(Math.max(8, anchor.left - 200), window.innerWidth - 580),
  };

  return createPortal(
    <div className="runn-allocation-popover" ref={popoverRef} style={style}>
      {!fixedPerson ? (
        <select
          className="runn-allocation-person"
          onChange={(event) => handlePersonChange(Number(event.target.value))}
          value={personId}
        >
          {people.map((person) => (
            <option key={person.id} value={person.id}>
              {person.name}
            </option>
          ))}
        </select>
      ) : null}

      <div className="runn-effort-row">
        <label className="runn-field">
          <span className="runn-field-label">
            Effort <HelpCircle aria-hidden size={13} />
          </span>
          <div className="runn-split">
            <input
              aria-label="Effort"
              onChange={(event) => setEffortInput(event.target.value)}
              type="text"
              value={effortInput}
            />
            <select
              aria-label="Effort unit"
              onChange={(event) => handleEffortUnitChange(event.target.value as EffortUnit)}
              value={effortUnit}
            >
              {EFFORT_UNITS.map((option) => (
                <option key={option.value} value={option.value}>
                  {option.label}
                </option>
              ))}
            </select>
          </div>
        </label>

        <label className="runn-field">
          <span className="runn-field-label">Work Days</span>
          <input
            aria-label="Work days"
            className="runn-workdays"
            min={1}
            onChange={(event) => setWorkDays(Math.max(1, Number(event.target.value) || 1))}
            type="number"
            value={workDays}
          />
        </label>

        <label className="runn-field">
          <span className="runn-field-label">Total Effort</span>
          <div className="runn-split">
            <input
              aria-label="Total effort"
              readOnly
              type="text"
              value={totalUnit === "hours" ? formatHoursClock(totalHours) : `${totalDays}`}
            />
            <select
              aria-label="Total unit"
              onChange={(event) => setTotalUnit(event.target.value as TotalEffortUnit)}
              value={totalUnit}
            >
              <option value="hours">Hours</option>
              <option value="days">Days</option>
            </select>
          </div>
        </label>
      </div>

      {!isEdit && repeatEnabled ? (
        <div className="runn-repeat-hint muted-text">
          Repeats weekly until{" "}
          {repeatEndMode === "on" ? format(new Date(repeatEndOn), "d MMM yyyy") : `${repeatOccurrences} times`}
        </div>
      ) : null}

      <button className="runn-note-toggle" onClick={() => setNoteOpen((open) => !open)} type="button">
        <ChevronRight className={noteOpen ? "is-open" : ""} size={14} />
        Note
      </button>
      {noteOpen ? (
        <textarea
          className="runn-note-input"
          onChange={(event) => setNote(event.target.value)}
          placeholder="Add a note…"
          rows={2}
          value={note}
        />
      ) : null}

      {datesOpen ? (
        <div className="runn-date-row">
          <label>
            Start
            <input onChange={(event) => setRangeStart(event.target.value)} type="date" value={rangeStart} />
          </label>
          <label>
            End
            <input onChange={(event) => setRangeEnd(event.target.value)} type="date" value={rangeEnd} />
          </label>
        </div>
      ) : null}

      <div className="runn-popover-footer">
        {isEdit && onDelete ? (
          <button
            className="runn-delete"
            onClick={() => {
              if (window.confirm("Delete this assignment?")) onDelete();
            }}
            type="button"
          >
            Delete
          </button>
        ) : (
          <button className="runn-delete" onClick={onCancel} type="button">
            Cancel
          </button>
        )}

        <div className="runn-footer-actions">
          <button
            aria-label="Dates"
            className={`runn-icon-pill${datesOpen ? " is-active" : ""}`}
            onClick={() => {
              setDatesOpen((open) => !open);
              setMenuOpen(false);
              setTransferOpen(false);
            }}
            type="button"
          >
            <Calendar size={18} />
          </button>

          <div className="runn-menu-wrap" ref={menuRef}>
            <button
              aria-expanded={menuOpen}
              aria-label="More options"
              className={`runn-icon-pill${menuOpen ? " is-active" : ""}`}
              onClick={() => setMenuOpen((open) => !open)}
              type="button"
            >
              <MoreHorizontal size={18} />
            </button>
            {menuOpen ? (
              <div className="runn-menu runn-menu-actions">
                {isEdit ? (
                  <>
                    <button
                      className="runn-menu-action"
                      onClick={() => setTransferOpen((open) => !open)}
                      type="button"
                    >
                      Transfer
                    </button>
                    {transferOpen && transferPeople.length > 0 ? (
                      <div className="runn-menu-sub">
                        <select
                          onChange={(event) => {
                            const id = Number(event.target.value);
                            if (id) onTransfer?.(id);
                            setMenuOpen(false);
                            setTransferOpen(false);
                          }}
                          value=""
                        >
                          <option value="">Choose person…</option>
                          {transferPeople.map((person) => (
                            <option key={person.id} value={person.id}>
                              {person.name}
                            </option>
                          ))}
                        </select>
                      </div>
                    ) : null}
                    <button
                      className="runn-menu-action"
                      onClick={() => {
                        onClone?.();
                        setMenuOpen(false);
                      }}
                      type="button"
                    >
                      Clone
                    </button>
                    <button
                      className="runn-menu-action"
                      onClick={() => {
                        const last = dayDates.length
                          ? format(dayDates[dayDates.length - 1], "yyyy-MM-dd")
                          : rangeEnd;
                        setRangeEnd(last);
                        onSelectAllToRight?.();
                        setMenuOpen(false);
                      }}
                      type="button"
                    >
                      Select All to Right
                    </button>
                    <button
                      className="runn-menu-action"
                      onClick={() => {
                        onToggleMultiSelect?.();
                        setMenuOpen(false);
                      }}
                      type="button"
                    >
                      {multiSelectActive ? "Disable Multi-Select Mode" : "Enable Multi-Select Mode"}
                    </button>
                  </>
                ) : (
                  <>
                    <label className="runn-menu-item">
                      <input
                        checked={repeatEnabled}
                        onChange={(event) => setRepeatEnabled(event.target.checked)}
                        type="checkbox"
                      />
                      Repeat weekly
                    </label>
                    {repeatEnabled ? (
                      <>
                        <label className="runn-menu-item">
                          End on
                          <input
                            onChange={(event) => {
                              setRepeatEndMode("on");
                              setRepeatEndOn(event.target.value);
                            }}
                            type="date"
                            value={repeatEndOn}
                          />
                        </label>
                        <label className="runn-menu-item">
                          After
                          <input
                            min={1}
                            onChange={(event) => {
                              setRepeatEndMode("after");
                              setRepeatOccurrences(Math.max(1, Number(event.target.value) || 1));
                            }}
                            type="number"
                            value={repeatOccurrences}
                          />
                          times
                        </label>
                      </>
                    ) : null}
                  </>
                )}
              </div>
            ) : null}
          </div>

          <button
            className="runn-save"
            disabled={isSaving || !selectedPerson || personId === ""}
            onClick={handleSave}
            type="button"
          >
            Save
          </button>
        </div>
      </div>
    </div>,
    document.body,
  );
}
