"use client";

import React, { type RefObject } from "react";
import { clsx } from "clsx";
import { useEffect, useState } from "react";
import { createPortal } from "react-dom";
import type { AllocationLike, Assignment } from "@capakraken/shared";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidatePlanningViews } from "~/hooks/useInvalidatePlanningViews.js";
import { useViewportPopover } from "~/hooks/useViewportPopover.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { DateInput } from "~/components/ui/DateInput.js";

interface AllocationPopoverProps {
  allocationId: string;
  projectId: string;
  initialAllocation?: AllocationPopoverAssignment | null;
  onClose: () => void;
  onOpenPanel: (projectId: string) => void;
  /** Pixel position relative to the viewport */
  anchorX: number;
  anchorY: number;
  contextDate?: Date;
  ignoreScrollContainers?: RefObject<HTMLElement | null>[];
}

type AllocationPopoverAssignment = Assignment<AllocationLike>;

export function AllocationPopover({
  allocationId,
  projectId,
  initialAllocation = null,
  onClose,
  onOpenPanel,
  anchorX,
  anchorY,
  contextDate,
  ignoreScrollContainers,
}: AllocationPopoverProps) {
  const utils = trpc.useUtils();
  const invalidatePlanningViews = useInvalidatePlanningViews();
  const { ref, style } = useViewportPopover({
    anchor: { kind: "point", x: anchorX, y: anchorY },
    width: 300,
    estimatedHeight: 360,
    onClose,
    ...(ignoreScrollContainers ? { ignoreScrollContainers } : {}),
  });

  const shouldLoadAllocation = !initialAllocation;
  const allocationQuery = trpc.allocation.getAssignmentById.useQuery(
    { id: allocationId },
    {
      staleTime: 10_000,
      enabled: shouldLoadAllocation,
      retry: false,
    },
  );
  const fetchedAllocation = allocationQuery.data as AllocationPopoverAssignment | undefined;
  const allocation = initialAllocation ?? fetchedAllocation;
  const isLoading = shouldLoadAllocation && allocationQuery.isLoading;
  const allocationError = shouldLoadAllocation ? allocationQuery.error : null;

  const [hoursPerDay, setHoursPerDay] = useState<number | null>(null);
  const [startDate, setStartDate] = useState<string>("");
  const [endDate, setEndDate] = useState<string>("");
  const [includeSaturday, setIncludeSaturday] = useState(false);
  const [role, setRole] = useState("");
  const [carveStartDate, setCarveStartDate] = useState("");
  const [carveEndDate, setCarveEndDate] = useState("");

  useEffect(() => {
    if (allocation) {
      setHoursPerDay(allocation.hoursPerDay);
      setStartDate(toDateInput(new Date(allocation.startDate)));
      setEndDate(toDateInput(new Date(allocation.endDate)));
      const meta = allocation.metadata as { includeSaturday?: boolean } | null;
      setIncludeSaturday(meta?.includeSaturday ?? false);
      setRole(allocation.role ?? "");
      const defaultCarveDate = contextDate ? toDateInput(contextDate) : "";
      setCarveStartDate(defaultCarveDate);
      setCarveEndDate(defaultCarveDate);
    }
  }, [allocation, contextDate]);

  const updateMutation = trpc.timeline.updateAllocationInline.useMutation({
    onSuccess: () => {
      void invalidatePlanningViews();
      void utils.allocation.getAssignmentById.invalidate({ id: allocationId });
      onClose();
    },
  });

  const carveMutation = trpc.timeline.carveAllocationRange.useMutation({
    onSuccess: () => {
      void invalidatePlanningViews();
      void utils.allocation.getAssignmentById.invalidate({ id: allocationId });
      onClose();
    },
  });

  function toDateInput(d: Date): string {
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, "0");
    const day = String(d.getDate()).padStart(2, "0");
    return `${y}-${m}-${day}`;
  }

  function handleSave() {
    if (!allocation || hoursPerDay === null) return;
    updateMutation.mutate({
      allocationId: getPlanningEntryMutationId(allocation),
      hoursPerDay,
      startDate: startDate ? new Date(startDate) : undefined,
      endDate: endDate ? new Date(endDate) : undefined,
      includeSaturday,
      role: role || undefined,
    });
  }

  function handleCarveRange() {
    if (!allocation || !carveStartDate || !carveEndDate) return;
    carveMutation.mutate({
      allocationId: getPlanningEntryMutationId(allocation),
      startDate: new Date(carveStartDate),
      endDate: new Date(carveEndDate),
    });
  }

  if (isLoading) {
    const loadingPopover = (
      <div
        ref={ref}
        style={style}
        data-testid="timeline-allocation-popover-loading"
        className="bg-white border border-gray-200 rounded-xl shadow-xl p-4 text-sm text-gray-500"
      >
        Loading...
      </div>
    );
    return typeof document === "undefined"
      ? loadingPopover
      : createPortal(loadingPopover, document.body);
  }

  if (allocationError) {
    const errorPopover = (
      <div
        ref={ref}
        style={style}
        data-testid="timeline-allocation-popover-error"
        className="flex max-w-[300px] flex-col gap-3 rounded-xl border border-red-200 bg-white p-4 shadow-xl"
      >
        <div className="text-sm font-medium text-gray-800">Allocation unavailable</div>
        <p className="text-xs text-gray-500">The selected booking could not be loaded right now.</p>
        <p className="text-xs text-red-600">{allocationError.message}</p>
        <button
          onClick={() => {
            onClose();
            onOpenPanel(projectId);
          }}
          className="w-full rounded-lg bg-brand-600 px-3 py-2 text-sm font-medium text-white hover:bg-brand-700"
        >
          Open Project Panel
        </button>
      </div>
    );
    return typeof document === "undefined"
      ? errorPopover
      : createPortal(errorPopover, document.body);
  }

  if (!allocation) {
    const missingPopover = (
      <div
        ref={ref}
        style={style}
        data-testid="timeline-allocation-popover-unavailable"
        className="flex max-w-[300px] flex-col gap-3 rounded-xl border border-gray-200 bg-white p-4 shadow-xl"
      >
        <div className="text-sm font-medium text-gray-800">Allocation unavailable</div>
        <p className="text-xs text-gray-500">
          The selected booking could not be resolved from the current timeline data.
        </p>
        <button
          onClick={() => {
            onClose();
            onOpenPanel(projectId);
          }}
          className="w-full rounded-lg bg-brand-600 px-3 py-2 text-sm font-medium text-white hover:bg-brand-700"
        >
          Open Project Panel
        </button>
      </div>
    );
    return typeof document === "undefined"
      ? missingPopover
      : createPortal(missingPopover, document.body);
  }

  const dailyCostEUR = (
    ((hoursPerDay ?? allocation.hoursPerDay) * (allocation.resource?.lcrCents ?? 0)) /
    100
  ).toFixed(2);

  const carveDateRangeInvalid =
    Boolean(carveStartDate && carveEndDate) && carveEndDate < carveStartDate;

  const popover = (
    <div
      ref={ref}
      style={style}
      data-testid="timeline-allocation-popover"
      data-allocation-id={allocationId}
      className="flex max-h-[calc(100vh-32px)] flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xl"
    >
      {/* Header */}
      <div className="flex items-center justify-between px-4 py-3 bg-gray-50 border-b border-gray-100">
        <div>
          <span className="text-sm font-semibold text-gray-800">{role}</span>
        </div>
        <button
          onClick={onClose}
          aria-label="Close"
          className="text-gray-400 hover:text-gray-600 text-lg leading-none"
        >
          &times;
        </button>
      </div>

      <div className="space-y-3 overflow-y-auto p-4">
        {/* Resource */}
        <div className="text-xs text-gray-500">
          Resource:{" "}
          <span className="font-medium text-gray-700">{allocation.resource?.displayName}</span> ·{" "}
          <span className="text-gray-400">{allocation.resource?.eid}</span>
        </div>

        {/* Role */}
        <div>
          <label className="block text-xs font-medium text-gray-600 mb-1">Role</label>
          <input
            type="text"
            value={role}
            onChange={(e) => setRole(e.target.value)}
            className="w-full border border-gray-200 rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-brand-400"
          />
        </div>

        {/* Hours per day */}
        <div>
          <label className="block text-xs font-medium text-gray-600 mb-1">Hours / day</label>
          <input
            type="number"
            min={0.5}
            max={24}
            step={0.5}
            value={hoursPerDay ?? ""}
            onChange={(e) => setHoursPerDay(parseFloat(e.target.value))}
            className="w-full border border-gray-200 rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-brand-400"
          />
        </div>

        {/* Date range */}
        <div className="grid grid-cols-2 gap-2">
          <div>
            <label className="block text-xs font-medium text-gray-600 mb-1">Start</label>
            <DateInput
              value={startDate}
              onChange={setStartDate}
              className="w-full border border-gray-200 rounded-lg px-2 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-brand-400"
            />
          </div>
          <div>
            <label className="block text-xs font-medium text-gray-600 mb-1">End</label>
            <DateInput
              value={endDate}
              onChange={setEndDate}
              min={startDate}
              className="w-full border border-gray-200 rounded-lg px-2 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-brand-400"
            />
          </div>
        </div>

        {/* Include Saturday */}
        <label className="flex items-center gap-2 cursor-pointer">
          <input
            type="checkbox"
            checked={includeSaturday}
            onChange={(e) => setIncludeSaturday(e.target.checked)}
            className="rounded border-gray-300 text-brand-600 focus:ring-brand-400"
          />
          <span className="text-xs text-gray-700">Include Saturdays</span>
        </label>

        {/* Error */}
        {updateMutation.isError && (
          <p className="text-xs text-red-600">{updateMutation.error.message}</p>
        )}
        {carveMutation.isError && (
          <p className="text-xs text-red-600">{carveMutation.error.message}</p>
        )}

        {/* Actions */}
        <div className="flex items-center gap-2 pt-1">
          <button
            onClick={handleSave}
            disabled={updateMutation.isPending}
            className={clsx(
              "flex-1 py-1.5 rounded-lg text-sm font-medium transition-colors",
              "bg-brand-600 text-white hover:bg-brand-700 disabled:opacity-50",
            )}
          >
            {updateMutation.isPending ? "Saving…" : "Save"}
          </button>
          <button
            onClick={onClose}
            className="flex-1 py-1.5 rounded-lg text-sm font-medium border border-gray-200 text-gray-600 hover:bg-gray-50"
          >
            Cancel
          </button>
        </div>

        <div className="border-t border-gray-100 pt-3 space-y-2">
          <div className="flex items-center justify-between gap-2">
            <div>
              <div className="text-xs font-medium text-gray-700">Remove Date Range</div>
              <div className="text-[11px] text-gray-500">
                {contextDate
                  ? `Prefilled from ${toDateInput(contextDate)}`
                  : "Create a gap or split this booking."}
              </div>
            </div>
          </div>

          <div className="grid grid-cols-2 gap-2">
            <div>
              <label className="block text-xs font-medium text-gray-600 mb-1">From</label>
              <DateInput
                value={carveStartDate}
                onChange={setCarveStartDate}
                min={startDate}
                max={endDate}
                className="w-full border border-gray-200 rounded-lg px-2 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-red-300"
              />
            </div>
            <div>
              <label className="block text-xs font-medium text-gray-600 mb-1">To</label>
              <DateInput
                value={carveEndDate}
                onChange={setCarveEndDate}
                min={carveStartDate || startDate}
                max={endDate}
                className="w-full border border-gray-200 rounded-lg px-2 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-red-300"
              />
            </div>
          </div>

          {carveDateRangeInvalid && (
            <p className="text-xs text-red-600">End date must be on or after the start date.</p>
          )}

          <button
            onClick={handleCarveRange}
            disabled={
              carveMutation.isPending || !carveStartDate || !carveEndDate || carveDateRangeInvalid
            }
            className="w-full py-1.5 rounded-lg text-sm font-medium transition-colors bg-red-600 text-white hover:bg-red-700 disabled:opacity-50"
          >
            {carveMutation.isPending ? "Removing…" : "Remove Selected Range"}
          </button>
        </div>

        {/* Link to full panel */}
        <button
          onClick={() => {
            onClose();
            onOpenPanel(projectId);
          }}
          className="w-full text-xs text-brand-600 hover:text-brand-800 text-center pt-1"
        >
          Open Project Panel →
        </button>
      </div>
    </div>
  );

  return typeof document === "undefined" ? popover : createPortal(popover, document.body);
}
