"use client";

import { useRef, useState, useMemo } from "react";
import { AllocationStatus } from "@nexus/shared";
import { useFocusTrap } from "~/hooks/useFocusTrap.js";
import { formatCents, formatDateMedium } from "~/lib/format.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidatePlanningViews } from "~/hooks/useInvalidatePlanningViews.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";

interface OpenDemandAllocation {
  id: string;
  entityId?: string | null;
  sourceAllocationId?: string | null;
  projectId: string;
  roleId: string | null;
  role: string | null;
  headcount: number;
  startDate: Date | string;
  endDate: Date | string;
  hoursPerDay: number;
  budgetCents?: number;
  roleEntity?: { id: string; name: string; color: string | null } | null;
  project?: { id: string; name: string; shortCode: string };
}

interface FillOpenDemandModalProps {
  allocation: OpenDemandAllocation;
  onClose: () => void;
  onSuccess: () => void;
}

/** A planned (not yet submitted) resource assignment. */
interface PlannedResource {
  resourceId: string;
  resourceName: string;
  eid: string;
  hoursPerDay: number;
  availableHours: number;
  availableDays: number;
  conflictDays: number;
  coveragePercent: number;
  estimatedCostCents: number;
}

export function FillOpenDemandModal({ allocation, onClose, onSuccess }: FillOpenDemandModalProps) {
  // ─── Phase: "plan" (select resources) → "confirm" (review & submit) ──
  const [phase, setPhase] = useState<"plan" | "confirm">("plan");
  const [planned, setPlanned] = useState<PlannedResource[]>([]);

  // Planning phase state
  const [resourceId, setResourceId] = useState("");
  const [hoursPerDay, setHoursPerDay] = useState<number>(allocation.hoursPerDay);
  const [search, setSearch] = useState("");
  const [debouncedSearch, setDebouncedSearch] = useState("");
  const [serverError, setServerError] = useState<string | null>(null);

  // Submit phase state
  const [submitting, setSubmitting] = useState(false);
  const [submitProgress, setSubmitProgress] = useState(0);

  const panelRef = useRef<HTMLDivElement>(null);
  useFocusTrap(panelRef, true);

  const searchTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
  function handleSearchChange(value: string) {
    setSearch(value);
    clearTimeout(searchTimerRef.current);
    searchTimerRef.current = setTimeout(() => setDebouncedSearch(value), 200);
  }

  const invalidatePlanningViews = useInvalidatePlanningViews();

  const { data: resources } = trpc.resource.listStaff.useQuery(
    { isActive: true, search: debouncedSearch || undefined, limit: 50 },
    { staleTime: 15_000 },
  ) as {
    data:
      | { resources: Array<{ id: string; displayName: string; eid: string; lcrCents: number }> }
      | undefined;
  };

  const availabilityQuery = trpc.allocation.checkResourceAvailability.useQuery(
    {
      resourceId,
      startDate: new Date(allocation.startDate),
      endDate: new Date(allocation.endDate),
      hoursPerDay,
    },
    { enabled: !!resourceId && phase === "plan", staleTime: 10_000 },
  );

  const fillMutation = trpc.allocation.fillOpenDemandByAllocation.useMutation();

  const roleName = allocation.roleEntity?.name ?? allocation.role ?? "Unknown Role";
  const roleColor = allocation.roleEntity?.color ?? "#6366f1";
  const resourceList = resources?.resources ?? [];
  const avail = availabilityQuery.data;

  const totalDemandHours = useMemo(() => {
    let workingDays = 0;
    const d = new Date(allocation.startDate);
    const end = new Date(allocation.endDate);
    while (d <= end) {
      if (d.getDay() !== 0 && d.getDay() !== 6) workingDays++;
      d.setDate(d.getDate() + 1);
    }
    return workingDays * allocation.hoursPerDay;
  }, [allocation.startDate, allocation.endDate, allocation.hoursPerDay]);

  const consumedHours = planned.reduce((sum, r) => sum + r.availableHours, 0);
  const remainingHours = Math.max(0, totalDemandHours - consumedHours);

  // ─── Planning phase actions ──────────────────────────────────────────
  function addToPlan() {
    if (!resourceId || !avail) return;
    const selectedResource = resourceList.find((r) => r.id === resourceId);
    if (!selectedResource) return;

    // Estimated cost = LCR * available hours
    const lcrCents = selectedResource.lcrCents ?? 0;
    const estimatedCostCents = Math.round(lcrCents * avail.totalAvailableHours);

    setPlanned((prev) => [
      ...prev,
      {
        resourceId: selectedResource.id,
        resourceName: selectedResource.displayName,
        eid: selectedResource.eid,
        hoursPerDay,
        availableHours: avail.totalAvailableHours,
        availableDays: avail.availableDays,
        conflictDays: avail.conflictDays,
        coveragePercent: avail.coveragePercent,
        estimatedCostCents,
      },
    ]);

    // Reset for next resource
    setResourceId("");
    setSearch("");
    setDebouncedSearch("");
    setHoursPerDay(allocation.hoursPerDay);
  }

  function removeFromPlan(rid: string) {
    setPlanned((prev) => prev.filter((r) => r.resourceId !== rid));
  }

  // ─── Confirm phase: submit all planned resources sequentially ────────
  async function handleConfirmSubmit() {
    setSubmitting(true);
    setServerError(null);
    setSubmitProgress(0);

    const allocationId = getPlanningEntryMutationId(allocation);

    for (let i = 0; i < planned.length; i++) {
      const p = planned[i]!;
      setSubmitProgress(i + 1);
      try {
        await fillMutation.mutateAsync({
          allocationId,
          resourceId: p.resourceId,
          hoursPerDay: p.hoursPerDay,
          status: AllocationStatus.PROPOSED,
        });
      } catch (err) {
        setServerError(
          `Failed to assign ${p.resourceName}: ${err instanceof Error ? err.message : String(err)}`,
        );
        setSubmitting(false);
        return;
      }
    }

    await invalidatePlanningViews();
    setSubmitting(false);
    onSuccess();
  }

  const isAlreadyPlanned = (rid: string) => planned.some((p) => p.resourceId === rid);

  // ─── Render ──────────────────────────────────────────────────────────
  return (
    <div
      className="fixed inset-0 bg-black/50 z-50 flex items-start justify-center overflow-y-auto py-8"
      onClick={(e) => {
        if (e.target === e.currentTarget && !submitting) onClose();
      }}
    >
      <div
        ref={panelRef}
        className="bg-white dark:bg-gray-800 rounded-xl shadow-2xl w-full max-w-lg mx-4"
        onKeyDown={(e) => {
          if (e.key === "Escape" && !submitting) onClose();
        }}
      >
        {/* Header */}
        <div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
          <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-1">
            {phase === "plan" ? "Plan Demand Assignment" : "Confirm Assignments"}
            <InfoTooltip content="Fill an open demand by assigning one or more real resources to a placeholder staffing requirement. Each assignment creates a new allocation." />
          </h2>
          <button
            type="button"
            onClick={onClose}
            disabled={submitting}
            className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-xl leading-none disabled:opacity-30"
          >
            &times;
          </button>
        </div>

        <div className="px-6 pt-4 pb-2 space-y-3">
          {/* Demand summary */}
          <div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-3 flex items-start gap-3">
            <div
              className="w-3 h-3 rounded-full mt-1 flex-shrink-0"
              style={{ backgroundColor: roleColor }}
            />
            <div className="flex-1">
              <div className="font-medium text-gray-900 dark:text-gray-100">{roleName}</div>
              <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
                {allocation.project?.name} · {formatDateMedium(allocation.startDate)} –{" "}
                {formatDateMedium(allocation.endDate)}
              </div>
              <div className="text-xs text-gray-500 dark:text-gray-400">
                {allocation.hoursPerDay}h/day · {totalDemandHours.toLocaleString()}h total
                {allocation.budgetCents && allocation.budgetCents > 0
                  ? ` · Budget: ${formatCents(allocation.budgetCents)} EUR`
                  : ""}
              </div>
            </div>
          </div>

          {/* Coverage bar */}
          <div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-3">
            <div className="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mb-1.5">
              <span>Demand coverage</span>
              <span>
                {Math.round(consumedHours)}h / {totalDemandHours}h (
                {totalDemandHours > 0 ? Math.round((consumedHours / totalDemandHours) * 100) : 0}%)
              </span>
            </div>
            <div className="w-full h-2.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden flex">
              {planned.map((r, i) => (
                <div
                  key={r.resourceId}
                  className="h-full transition-all"
                  style={{
                    width: `${Math.min(100, (r.availableHours / totalDemandHours) * 100)}%`,
                    backgroundColor: `hsl(${(i * 60 + 200) % 360}, 60%, 55%)`,
                  }}
                  title={`${r.resourceName}: ${Math.round(r.availableHours)}h`}
                />
              ))}
            </div>

            {/* Planned resources list */}
            {planned.length > 0 && (
              <div className="mt-2 space-y-1">
                {planned.map((r, i) => (
                  <div key={r.resourceId} className="flex items-center gap-2 text-xs group">
                    <div
                      className="w-2 h-2 rounded-full flex-shrink-0"
                      style={{ backgroundColor: `hsl(${(i * 60 + 200) % 360}, 60%, 55%)` }}
                    />
                    <span className="text-gray-700 dark:text-gray-300 font-medium">
                      {r.resourceName}
                    </span>
                    <span className="text-gray-400">({r.eid})</span>
                    <span className="text-gray-500">{r.hoursPerDay}h/day</span>
                    <span className="ml-auto text-gray-500">
                      {Math.round(r.availableHours)}h · {r.coveragePercent}%
                    </span>
                    {phase === "plan" && (
                      <button
                        type="button"
                        onClick={() => removeFromPlan(r.resourceId)}
                        className="text-red-400 hover:text-red-600 opacity-0 group-hover:opacity-100 transition-opacity"
                        title="Remove"
                      >
                        &times;
                      </button>
                    )}
                  </div>
                ))}
                {remainingHours > 0 && (
                  <div className="flex items-center gap-2 text-xs">
                    <div className="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-600 flex-shrink-0" />
                    <span className="text-amber-600 dark:text-amber-400 font-medium">
                      Remaining: {Math.round(remainingHours)}h
                    </span>
                  </div>
                )}
              </div>
            )}
          </div>
        </div>

        {/* ─── PLAN PHASE: add resources ──────────────────────────────── */}
        {phase === "plan" && (
          <div className="px-6 pb-5 space-y-4">
            <div>
              <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                Search Resource
              </label>
              <input
                type="text"
                placeholder="Search by name or EID..."
                value={search}
                onChange={(e) => handleSearchChange(e.target.value)}
                className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm dark:bg-gray-900 dark:text-gray-100"
              />
            </div>

            <div>
              <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                Select Resource
              </label>
              <select
                value={resourceId}
                onChange={(e) => setResourceId(e.target.value)}
                className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm dark:bg-gray-900 dark:text-gray-100"
                size={Math.min(6, Math.max(3, resourceList.length))}
              >
                <option value="">Select a resource...</option>
                {resourceList.map((r) => {
                  const alreadyPlanned = isAlreadyPlanned(r.id);
                  return (
                    <option key={r.id} value={r.id} disabled={alreadyPlanned}>
                      {r.displayName} ({r.eid}){alreadyPlanned ? " (planned)" : ""}
                    </option>
                  );
                })}
              </select>
            </div>

            <div>
              <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                Hours / Day
              </label>
              <input
                type="number"
                value={hoursPerDay}
                onChange={(e) => setHoursPerDay(Number(e.target.value))}
                min={0.5}
                max={24}
                step={0.5}
                className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm dark:bg-gray-900 dark:text-gray-100"
              />
            </div>

            {/* Availability preview */}
            {resourceId && avail && (
              <div
                className={`rounded-lg p-3 border text-sm ${
                  avail.coveragePercent >= 100
                    ? "bg-green-50 border-green-200 dark:bg-green-900/20 dark:border-green-800"
                    : avail.coveragePercent >= 50
                      ? "bg-amber-50 border-amber-200 dark:bg-amber-900/20 dark:border-amber-800"
                      : "bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800"
                }`}
              >
                <div className="font-medium text-gray-900 dark:text-gray-100 mb-1.5">
                  Availability: {avail.resource.name}
                </div>
                <div className="grid grid-cols-3 gap-2 text-xs">
                  <div>
                    <span className="text-gray-500 dark:text-gray-400">Available</span>
                    <div className="font-semibold text-green-700 dark:text-green-400">
                      {avail.availableDays} days
                    </div>
                  </div>
                  <div>
                    <span className="text-gray-500 dark:text-gray-400">Conflicts</span>
                    <div className="font-semibold text-red-700 dark:text-red-400">
                      {avail.conflictDays} days
                    </div>
                  </div>
                  <div>
                    <span className="text-gray-500 dark:text-gray-400">Hours</span>
                    <div className="font-semibold text-gray-900 dark:text-gray-100">
                      {avail.totalAvailableHours}h / {avail.totalRequestedHours}h
                    </div>
                  </div>
                </div>

                {avail.existingAssignments.length > 0 && (
                  <div className="mt-2 pt-2 border-t border-gray-200 dark:border-gray-700">
                    <div className="text-xs text-gray-500 dark:text-gray-400 mb-1">
                      Existing bookings:
                    </div>
                    {avail.existingAssignments.slice(0, 4).map((a, i) => (
                      <div key={i} className="text-xs text-gray-600 dark:text-gray-300">
                        {a.code} · {a.hoursPerDay}h/day · {a.start} – {a.end}
                      </div>
                    ))}
                    {avail.existingAssignments.length > 4 && (
                      <div className="text-xs text-gray-400">
                        +{avail.existingAssignments.length - 4} more
                      </div>
                    )}
                  </div>
                )}
              </div>
            )}

            {resourceId && availabilityQuery.isLoading && (
              <div className="text-xs text-gray-400 dark:text-gray-500 animate-pulse">
                Checking availability...
              </div>
            )}

            {/* Action buttons */}
            <div className="flex items-center justify-between gap-3 pt-2">
              <button
                type="button"
                onClick={onClose}
                className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100"
              >
                Cancel
              </button>
              <div className="flex items-center gap-2">
                <button
                  type="button"
                  onClick={addToPlan}
                  disabled={!resourceId || !avail || isAlreadyPlanned(resourceId)}
                  className="px-4 py-2 border border-brand-600 text-brand-600 rounded-lg hover:bg-brand-50 dark:hover:bg-brand-900/20 text-sm font-medium disabled:opacity-40 disabled:cursor-not-allowed"
                >
                  + Queue Assignment
                </button>
                {planned.length > 0 && (
                  <button
                    type="button"
                    onClick={() => setPhase("confirm")}
                    className="px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium"
                  >
                    Review Queued ({planned.length})
                  </button>
                )}
              </div>
            </div>
          </div>
        )}

        {/* ─── CONFIRM PHASE: review & submit all ────────────────────── */}
        {phase === "confirm" && (
          <div className="px-6 pb-5 space-y-4">
            <div className="rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
              <table className="w-full text-sm">
                <thead className="bg-gray-50 dark:bg-gray-900">
                  <tr>
                    <th className="px-3 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400">
                      Resource
                    </th>
                    <th className="px-3 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400">
                      h/day
                    </th>
                    <th className="px-3 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400">
                      Hours
                    </th>
                    <th className="px-3 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400">
                      <span className="inline-flex items-center justify-end gap-0.5">
                        Est. Cost
                        <InfoTooltip content="Estimated cost = resource LCR x available hours in the demand period." />
                      </span>
                    </th>
                    <th className="px-3 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400">
                      <span className="inline-flex items-center justify-end gap-0.5">
                        Coverage
                        <InfoTooltip content="Percentage of the demand period this resource can cover, accounting for existing bookings." />
                      </span>
                    </th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-gray-100 dark:divide-gray-800">
                  {planned.map((r) => (
                    <tr key={r.resourceId}>
                      <td className="px-3 py-2 font-medium text-gray-900 dark:text-gray-100">
                        {r.resourceName}
                        <span className="ml-1 text-xs text-gray-400 font-mono">{r.eid}</span>
                      </td>
                      <td className="px-3 py-2 text-right text-gray-600 dark:text-gray-300">
                        {r.hoursPerDay}h
                      </td>
                      <td className="px-3 py-2 text-right text-gray-600 dark:text-gray-300">
                        {Math.round(r.availableHours)}h
                      </td>
                      <td className="px-3 py-2 text-right text-gray-600 dark:text-gray-300">
                        {formatCents(r.estimatedCostCents)} EUR
                      </td>
                      <td className="px-3 py-2 text-right">
                        <span
                          className={`font-medium ${r.coveragePercent >= 100 ? "text-green-600" : r.coveragePercent >= 50 ? "text-amber-600" : "text-red-600"}`}
                        >
                          {r.coveragePercent}%
                        </span>
                      </td>
                    </tr>
                  ))}
                </tbody>
                <tfoot className="bg-gray-50 dark:bg-gray-900">
                  <tr>
                    <td className="px-3 py-2 text-xs font-semibold text-gray-700 dark:text-gray-300">
                      Total
                    </td>
                    <td className="px-3 py-2" />
                    <td className="px-3 py-2 text-right text-xs font-semibold text-gray-700 dark:text-gray-300">
                      {Math.round(consumedHours)}h / {totalDemandHours}h
                    </td>
                    <td className="px-3 py-2 text-right text-xs font-semibold text-gray-700 dark:text-gray-300">
                      {formatCents(planned.reduce((s, r) => s + r.estimatedCostCents, 0))} EUR
                    </td>
                    <td className="px-3 py-2 text-right text-xs font-semibold text-gray-700 dark:text-gray-300">
                      {totalDemandHours > 0
                        ? Math.round((consumedHours / totalDemandHours) * 100)
                        : 0}
                      %
                    </td>
                  </tr>
                  {allocation.budgetCents && allocation.budgetCents > 0 && (
                    <tr>
                      <td
                        colSpan={3}
                        className="px-3 py-1.5 text-right text-xs text-gray-500 dark:text-gray-400"
                      >
                        Role Budget:
                      </td>
                      <td className="px-3 py-1.5 text-right text-xs font-semibold text-gray-700 dark:text-gray-300">
                        {formatCents(allocation.budgetCents)} EUR
                      </td>
                      <td className="px-3 py-1.5 text-right text-xs">
                        {(() => {
                          const totalCost = planned.reduce((s, r) => s + r.estimatedCostCents, 0);
                          const remain = allocation.budgetCents! - totalCost;
                          return (
                            <span
                              className={remain < 0 ? "text-red-600 font-medium" : "text-green-600"}
                            >
                              {remain < 0
                                ? `${formatCents(Math.abs(remain))} over`
                                : `${formatCents(remain)} left`}
                            </span>
                          );
                        })()}
                      </td>
                    </tr>
                  )}
                </tfoot>
              </table>
            </div>

            {remainingHours > 0 && (
              <div className="text-xs text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-900/20 rounded-lg px-3 py-2 border border-amber-200 dark:border-amber-800">
                {Math.round(remainingHours)}h remain uncovered. You can add more resources or assign
                partially.
              </div>
            )}

            {submitting && (
              <div className="text-sm text-brand-600 dark:text-brand-400 animate-pulse">
                Assigning resource {submitProgress}/{planned.length}...
              </div>
            )}

            {serverError && (
              <div className="rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 px-4 py-3 text-sm text-red-700 dark:text-red-400">
                {serverError}
              </div>
            )}

            <div className="flex items-center justify-between gap-3 pt-2">
              <button
                type="button"
                onClick={() => setPhase("plan")}
                disabled={submitting}
                className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 disabled:opacity-50"
              >
                Back to Planning
              </button>
              <button
                type="button"
                onClick={() => void handleConfirmSubmit()}
                disabled={submitting || planned.length === 0}
                className="px-5 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-semibold disabled:opacity-50"
              >
                {submitting
                  ? `Assigning ${submitProgress}/${planned.length}...`
                  : `Confirm & Assign ${planned.length} Resource${planned.length !== 1 ? "s" : ""}`}
              </button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}
