"use client";

import { createPortal } from "react-dom";
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
import { clsx } from "clsx";
import type { StaffingRequirement } from "@capakraken/shared";
import { BlueprintTarget, OrderType, AllocationType, ProjectStatus, AllocationStatus } from "@capakraken/shared";
import { trpc } from "~/lib/trpc/client.js";
import { uuid } from "~/lib/uuid.js";
import { DateInput } from "~/components/ui/DateInput.js";
import { SkillTagInput } from "~/components/ui/SkillTagInput.js";
import { usePermissions } from "~/hooks/usePermissions.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
import { formatCents } from "~/lib/format.js";
import { ConfettiBurst } from "~/components/ui/ConfettiBurst.js";
import { SuccessToast } from "~/components/ui/SuccessToast.js";
import { useAnchoredOverlay } from "~/hooks/useAnchoredOverlay.js";

// ─── Constants ────────────────────────────────────────────────────────────────

const ORDER_TYPE_OPTIONS = [
  { value: "BD", label: "BD" },
  { value: "CHARGEABLE", label: "Chargeable" },
  { value: "INTERNAL", label: "Internal" },
  { value: "OVERHEAD", label: "Overhead" },
] as const;

const ALLOCATION_TYPE_OPTIONS = [
  { value: "INT", label: "INT" },
  { value: "EXT", label: "EXT" },
] as const;

const INPUT_CLS =
  "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm w-full";

const SELECT_CLS =
  "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm w-full bg-white";

const LABEL_CLS = "block text-xs font-medium text-gray-600 mb-1";

const BTN_PRIMARY =
  "px-5 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50 transition-colors";

const BTN_SECONDARY =
  "px-5 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 text-sm font-medium transition-colors";

const BTN_DANGER =
  "px-2 py-1 text-red-500 hover:text-red-700 hover:bg-red-50 rounded text-sm transition-colors";

// ─── Types ────────────────────────────────────────────────────────────────────

interface Assignment {
  requirementId: string;
  resourceId: string;
  resourceName: string;
  role: string;
}

interface WizardState {
  blueprintId: string | null;
  shortCode: string;
  name: string;
  orderType: string;
  allocationType: string;
  startDate: string;
  endDate: string;
  budgetEur: string;
  winProbability: number;
  responsiblePerson: string;
  staffingReqs: StaffingRequirement[];
  assignments: Assignment[];
  saveAsDraft: boolean;
}

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

function makeDefaultState(): WizardState {
  const today = formatDateForInput(new Date());
  return {
    blueprintId: null,
    shortCode: "",
    name: "",
    orderType: "CHARGEABLE",
    allocationType: "INT",
    startDate: today,
    endDate: today,
    budgetEur: "",
    winProbability: 100,
    responsiblePerson: "",
    staffingReqs: [],
    assignments: [],
    saveAsDraft: true,
  };
}

function makeReq(): StaffingRequirement {
  return {
    id: uuid(),
    role: "",
    requiredSkills: [],
    preferredSkills: [],
    hoursPerDay: 8,
    headcount: 1,
  };
}

// ─── Step indicators ─────────────────────────────────────────────────────────

const STEPS = [
  "Blueprint & Identity",
  "Timeline & Budget",
  "Staffing Demand",
  "Suggestions",
  "Review & Create",
];

function StepBar({ current }: { current: number }) {
  return (
    <div className="flex items-center gap-0 mb-6">
      {STEPS.map((label, idx) => (
        <div key={idx} className="flex items-center flex-1 min-w-0">
          <div className="flex flex-col items-center flex-shrink-0">
            <div
              className={clsx(
                "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold transition-colors",
                idx < current
                  ? "bg-brand-600 text-white"
                  : idx === current
                    ? "bg-brand-600 text-white ring-4 ring-brand-100"
                    : "bg-gray-100 text-gray-400",
              )}
            >
              {idx < current ? "✓" : idx + 1}
            </div>
            <span
              className={clsx(
                "mt-1 text-[10px] text-center whitespace-nowrap leading-tight max-w-[72px]",
                idx === current ? "text-brand-600 font-medium" : "text-gray-400",
              )}
            >
              {label}
            </span>
          </div>
          {idx < STEPS.length - 1 && (
            <div
              className={clsx(
                "h-0.5 flex-1 mx-1 mt-[-14px]",
                idx < current ? "bg-brand-400" : "bg-gray-200",
              )}
            />
          )}
        </div>
      ))}
    </div>
  );
}

// ─── Step 1: Blueprint & Identity ────────────────────────────────────────────

interface Step1Props {
  state: WizardState;
  onChange: (patch: Partial<WizardState>) => void;
}

function Step1({ state, onChange }: Step1Props) {
  const { data: blueprints } = trpc.blueprint.list.useQuery(
    { target: BlueprintTarget.PROJECT, isActive: true },
    { staleTime: 30_000 },
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ) as { data: Array<{ id: string; name: string; description?: string | null; rolePresets?: unknown }> | undefined };

  const selectedBp = blueprints?.find((b) => b.id === state.blueprintId);

  function selectBlueprint(id: string | null) {
    if (!id) {
      onChange({ blueprintId: null });
      return;
    }
    const bp = blueprints?.find((b) => b.id === id);
    const presets = Array.isArray(bp?.rolePresets)
      ? (bp.rolePresets as unknown as StaffingRequirement[])
      : [];
    onChange({ blueprintId: id, staffingReqs: presets });
  }

  return (
    <div className="space-y-5">
      {/* Blueprint picker */}
      <div>
        <label className={LABEL_CLS}>Project Blueprint (optional)<InfoTooltip content="Blueprints are templates that pre-fill role presets and default settings. Selecting one loads staffing requirements into Step 3." /></label>
        <div className="grid grid-cols-2 sm:grid-cols-3 gap-2 mb-2">
          <button
            type="button"
            onClick={() => selectBlueprint(null)}
            className={clsx(
              "text-left p-3 rounded-lg border text-sm transition-colors",
              !state.blueprintId
                ? "border-brand-500 bg-brand-50 text-brand-700"
                : "border-gray-200 hover:border-gray-300 text-gray-600",
            )}
          >
            <div className="font-medium">No Blueprint</div>
            <div className="text-xs text-gray-400 mt-0.5">Start blank</div>
          </button>
          {(blueprints ?? []).map((bp) => (
            <button
              key={bp.id}
              type="button"
              onClick={() => selectBlueprint(bp.id)}
              className={clsx(
                "text-left p-3 rounded-lg border text-sm transition-colors",
                state.blueprintId === bp.id
                  ? "border-brand-500 bg-brand-50 text-brand-700"
                  : "border-gray-200 hover:border-gray-300 text-gray-600",
              )}
            >
              <div className="font-medium truncate">{bp.name}</div>
              {bp.description && (
                <div className="text-xs text-gray-400 mt-0.5 truncate">{bp.description}</div>
              )}
              {Array.isArray(bp.rolePresets) && bp.rolePresets.length > 0 && (
                <div className="text-xs text-brand-500 mt-1">
                  {bp.rolePresets.length} role preset{bp.rolePresets.length !== 1 ? "s" : ""}
                </div>
              )}
            </button>
          ))}
        </div>
        {selectedBp && (
          <p className="text-xs text-brand-600">
            Selected: <strong>{selectedBp.name}</strong>
            {Array.isArray(selectedBp.rolePresets) && selectedBp.rolePresets.length > 0
              ? ` — ${selectedBp.rolePresets.length} role presets will be loaded in Step 3`
              : ""}
          </p>
        )}
      </div>

      <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
        {/* Short code */}
        <div>
          <label className={LABEL_CLS}>Short Code *<InfoTooltip content="Unique chargecode for this project, used for time tracking and cost attribution. Must be uppercase alphanumeric." /></label>
          <input
            type="text"
            value={state.shortCode}
            onChange={(e) => onChange({ shortCode: e.target.value.toUpperCase() })}
            placeholder="e.g. BMW26D"
            className={INPUT_CLS}
          />
          <p className="text-xs text-gray-400 mt-0.5">Uppercase alphanumeric, max 20 chars</p>
        </div>

        {/* Name */}
        <div>
          <label className={LABEL_CLS}>Project Name *<InfoTooltip content="Display name shown on the timeline and in reports." /></label>
          <input
            type="text"
            value={state.name}
            onChange={(e) => onChange({ name: e.target.value })}
            placeholder="e.g. BMW X3 Campaign"
            className={INPUT_CLS}
          />
        </div>

        {/* Order type */}
        <div>
          <label className={LABEL_CLS}>Order Type *<InfoTooltip content="BD = Business Development (pre-sales), CHARGEABLE = billable client work, INTERNAL = internal project, OVERHEAD = non-project overhead." /></label>
          <select
            value={state.orderType}
            onChange={(e) => onChange({ orderType: e.target.value })}
            className={SELECT_CLS}
          >
            {ORDER_TYPE_OPTIONS.map((opt) => (
              <option key={opt.value} value={opt.value}>
                {opt.label}
              </option>
            ))}
          </select>
        </div>

        {/* Allocation type */}
        <div>
          <label className={LABEL_CLS}>Allocation Type *<InfoTooltip content="INT = staffed with internal resources, EXT = staffed with external contractors or freelancers." /></label>
          <select
            value={state.allocationType}
            onChange={(e) => onChange({ allocationType: e.target.value })}
            className={SELECT_CLS}
          >
            {ALLOCATION_TYPE_OPTIONS.map((opt) => (
              <option key={opt.value} value={opt.value}>
                {opt.label}
              </option>
            ))}
          </select>
        </div>
      </div>
    </div>
  );
}

// ─── Responsible Person Picker ────────────────────────────────────────────────

function ResourcePersonPicker({ value, onChange }: { value: string; onChange: (v: string) => void }) {
  const [query, setQuery] = useState(value);
  const [open, setOpen] = useState(false);
  const [debouncedSearch, setDebouncedSearch] = useState("");
  const inputRef = useRef<HTMLInputElement>(null);

  // Debounce search query to avoid excessive API calls
  useEffect(() => {
    const timer = setTimeout(() => setDebouncedSearch(query), 200);
    return () => clearTimeout(timer);
  }, [query]);

  // Server-side search — no client-side limit, searches full database
  const { data } = trpc.resource.directory.useQuery(
    { isActive: true, search: debouncedSearch || undefined, limit: 30 },
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    { staleTime: 15_000, placeholderData: (prev: any) => prev },
  );
  const filtered = useMemo(
    () => (data?.resources ?? []) as unknown as Array<{ id: string; displayName: string; eid: string }>,
    [data],
  );

  // Sync local query when external value changes (e.g. wizard reset)
  useEffect(() => {
    setQuery(value);
  }, [value]);

  const { panelRef, position } = useAnchoredOverlay<HTMLInputElement>({
    open,
    onClose: () => setOpen(false),
    align: "start",
    matchTriggerWidth: true,
    triggerRef: inputRef,
  });

  return (
    <div className="relative">
      <input
        ref={inputRef}
        type="text"
        value={query}
        onChange={(e) => {
          setQuery(e.target.value);
          onChange(e.target.value);
          setOpen(true);
        }}
        onFocus={() => setOpen(true)}
        placeholder="Search by name or EID…"
        className={INPUT_CLS}
      />
      {open && filtered.length > 0 && typeof document !== "undefined"
        ? createPortal(
            <div
              ref={panelRef}
              className="fixed z-[9998] max-h-48 overflow-y-auto rounded-lg border border-gray-200 bg-white shadow-lg"
              style={{
                top: position.top,
                left: position.left,
                width: position.minWidth,
              }}
              onMouseDown={(e) => e.preventDefault()}
            >
              <ul>
                {filtered.map((r) => (
                  <li key={r.id}>
                    <button
                      type="button"
                      onMouseDown={() => {
                        onChange(r.displayName);
                        setQuery(r.displayName);
                        setOpen(false);
                      }}
                      className="flex w-full items-baseline gap-2 px-3 py-2 text-left text-sm transition-colors hover:bg-gray-50"
                    >
                      <span className="truncate">{r.displayName}</span>
                      <span className="shrink-0 font-mono text-xs text-gray-400">{r.eid}</span>
                    </button>
                  </li>
                ))}
              </ul>
            </div>,
            document.body,
          )
        : null}
    </div>
  );
}

// ─── Step 2: Timeline & Budget ────────────────────────────────────────────────

interface Step2Props {
  state: WizardState;
  onChange: (patch: Partial<WizardState>) => void;
}

function Step2({ state, onChange }: Step2Props) {
  return (
    <div className="space-y-4">
      <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
        <div>
          <label className={LABEL_CLS}>Start Date *<InfoTooltip content="First day of the project period. Assignments and budget tracking begin from this date." /></label>
          <DateInput
            value={state.startDate}
            onChange={(v) => onChange({ startDate: v })}
          />
        </div>
        <div>
          <label className={LABEL_CLS}>End Date *<InfoTooltip content="Last day of the project period. Defines the timeline boundary for all assignments." /></label>
          <DateInput
            value={state.endDate}
            onChange={(v) => onChange({ endDate: v })}
          />
        </div>
      </div>

      <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
        <div>
          <label className={LABEL_CLS}>Budget (EUR)<InfoTooltip content="Total project budget in EUR. Stored internally as cents. Used to track spending against assignments." /></label>
          <input
            type="number"
            min={0}
            step={100}
            value={state.budgetEur}
            onChange={(e) => onChange({ budgetEur: e.target.value })}
            placeholder="e.g. 50000"
            className={INPUT_CLS}
          />
        </div>
        <div>
          <label className={LABEL_CLS}>Responsible Person<InfoTooltip content="Project lead or account manager. Search by name or employee ID." /></label>
          <ResourcePersonPicker
            value={state.responsiblePerson}
            onChange={(v) => onChange({ responsiblePerson: v })}
          />
        </div>
      </div>

      <div>
        <label className={LABEL_CLS}>
          Win Probability: <strong>{state.winProbability}%</strong>
          <InfoTooltip content="Likelihood of winning this project (0-100%). Affects the weighted pipeline value: budget x probability." />
        </label>
        <input
          type="range"
          min={0}
          max={100}
          step={5}
          value={state.winProbability}
          onChange={(e) => onChange({ winProbability: parseInt(e.target.value, 10) })}
          className="w-full accent-brand-600"
        />
        <div className="flex justify-between text-xs text-gray-400 mt-0.5">
          <span>0% (Unlikely)</span>
          <span>100% (Confirmed)</span>
        </div>
      </div>
    </div>
  );
}

// ─── Step 3: Staffing Demand ─────────────────────────────────────────────────

interface Step3Props {
  state: WizardState;
  onChange: (patch: Partial<WizardState>) => void;
}

function Step3({ state, onChange }: Step3Props) {
  const { data: rolesData } = trpc.role.list.useQuery(
    { isActive: true },
    { staleTime: 30_000 },
  );
  const roles = rolesData ?? [];

  function updateReq(idx: number, patch: Partial<StaffingRequirement>) {
    const next = state.staffingReqs.map((r, i) =>
      i === idx ? { ...r, ...patch } : r,
    );
    onChange({ staffingReqs: next });
  }

  function addReq() {
    onChange({ staffingReqs: [...state.staffingReqs, makeReq()] });
  }

  function removeReq(idx: number) {
    onChange({ staffingReqs: state.staffingReqs.filter((_, i) => i !== idx) });
  }

  return (
    <div>
      {state.blueprintId && state.staffingReqs.length > 0 && (
        <div className="mb-3 px-3 py-2 bg-brand-50 border border-brand-200 rounded-lg text-xs text-brand-700">
          Blueprint presets auto-loaded. Edit as needed.
        </div>
      )}

      {/* Budget allocation summary */}
      {state.budgetEur && parseFloat(state.budgetEur) > 0 && state.staffingReqs.length > 0 && (() => {
        const projectBudgetCents = Math.round(parseFloat(state.budgetEur || "0") * 100);
        const allocatedCents = state.staffingReqs.reduce((sum, r) => sum + (r.budgetCents ?? 0), 0);
        const remainingCents = projectBudgetCents - allocatedCents;
        const pct = projectBudgetCents > 0 ? Math.round((allocatedCents / projectBudgetCents) * 100) : 0;
        return (
          <div className={`mb-3 rounded-lg border p-3 text-xs ${remainingCents < 0 ? "bg-red-50 border-red-200 text-red-700" : remainingCents === 0 ? "bg-green-50 border-green-200 text-green-700" : "bg-amber-50 border-amber-200 text-amber-700"}`}>
            <div className="flex items-center justify-between mb-1.5">
              <span className="font-semibold">Budget Allocation</span>
              <span>{pct}% allocated</span>
            </div>
            <div className="w-full h-1.5 bg-gray-200 rounded-full overflow-hidden mb-1.5">
              <div className={`h-full rounded-full transition-all ${remainingCents < 0 ? "bg-red-500" : remainingCents === 0 ? "bg-green-500" : "bg-amber-500"}`} style={{ width: `${Math.min(100, pct)}%` }} />
            </div>
            <div className="flex justify-between">
              <span>Project: {formatCents(projectBudgetCents)} EUR</span>
              <span>Allocated: {formatCents(allocatedCents)} EUR</span>
              <span className="font-semibold">Remaining: {formatCents(remainingCents)} EUR</span>
            </div>
          </div>
        );
      })()}

      <div className="space-y-3 max-h-[45vh] overflow-y-auto pr-1">
        {state.staffingReqs.length === 0 && (
          <p className="text-sm text-gray-400 text-center py-8">
            No requirements yet. Click &ldquo;+ Add Role&rdquo; to define staffing needs.
          </p>
        )}
        {state.staffingReqs.map((req, idx) => (
          <div key={req.id} className="border border-gray-200 rounded-lg p-3 bg-white">
            <div className="flex flex-wrap items-start gap-2">
              <div className="flex-1 min-w-32">
                <label className="text-xs text-gray-400">Role *<InfoTooltip content="Select a predefined role or enter a custom role name. Defines the skill profile for this staffing demand." /></label>
                {roles.length > 0 ? (
                  <select
                    value={req.roleId ?? ""}
                    onChange={(e) => {
                      const selectedId = e.target.value;
                      const matched = roles.find((ro) => ro.id === selectedId);
                      if (selectedId && matched) {
                        updateReq(idx, { roleId: matched.id, role: matched.name });
                      } else {
                        // Clear roleId — rebuild without the key
                        const { roleId: _r, ...rest } = state.staffingReqs[idx]!;
                        void _r;
                        onChange({ staffingReqs: state.staffingReqs.map((r, i) => i === idx ? rest : r) });
                      }
                    }}
                    className={SELECT_CLS}
                  >
                    <option value="">Custom / Free text…</option>
                    {roles.map((ro) => (
                      <option key={ro.id} value={ro.id}>{ro.name}</option>
                    ))}
                  </select>
                ) : null}
                {(!req.roleId) && (
                  <input
                    type="text"
                    value={req.role}
                    onChange={(e) => updateReq(idx, { role: e.target.value })}
                    placeholder="e.g. 3D Artist"
                    className={clsx(INPUT_CLS, roles.length > 0 && "mt-1")}
                  />
                )}
              </div>
              <div className="w-20">
                <label className="text-xs text-gray-400">h/day<InfoTooltip content="Planned working hours per day for this role." /></label>
                <input
                  type="number"
                  value={req.hoursPerDay}
                  min={0}
                  max={24}
                  step={0.5}
                  onChange={(e) => updateReq(idx, { hoursPerDay: parseFloat(e.target.value) || 0 })}
                  className={INPUT_CLS}
                />
              </div>
              <div className="w-16">
                <label className="text-xs text-gray-400">Count<InfoTooltip content="Number of people needed for this role. Unfilled seats become placeholder demands until assigned." /></label>
                <input
                  type="number"
                  value={req.headcount}
                  min={1}
                  max={20}
                  onChange={(e) => updateReq(idx, { headcount: parseInt(e.target.value, 10) || 1 })}
                  className={INPUT_CLS}
                />
              </div>
              <div className="w-28">
                <label className="text-xs text-gray-400">Budget (EUR)<InfoTooltip content="Optional budget cap for this role. Tracked against actual assignment costs." /></label>
                <input
                  type="number"
                  value={req.budgetCents ? req.budgetCents / 100 : ""}
                  min={0}
                  step={100}
                  onChange={(e) => {
                    const val = parseFloat(e.target.value);
                    updateReq(idx, { budgetCents: Number.isFinite(val) && val > 0 ? Math.round(val * 100) : 0 } as Partial<StaffingRequirement>);
                  }}
                  placeholder="0"
                  className={INPUT_CLS}
                />
              </div>
              <div className="pt-5">
                <button
                  type="button"
                  onClick={() => removeReq(idx)}
                  className={BTN_DANGER}
                >
                  ×
                </button>
              </div>
            </div>
            <div className="grid grid-cols-1 sm:grid-cols-2 gap-2 mt-2">
              <div>
                <label className="text-xs text-gray-400">Required skills<InfoTooltip content="Skills a resource must have to be suggested for this role." /></label>
                <SkillTagInput
                  value={req.requiredSkills}
                  onChange={(skills) => updateReq(idx, { requiredSkills: skills })}
                  placeholder="Add required skill…"
                />
              </div>
              <div>
                <label className="text-xs text-gray-400">Preferred skills (optional)<InfoTooltip content="Nice-to-have skills that boost a resource's match score but are not mandatory." /></label>
                <SkillTagInput
                  value={req.preferredSkills ?? []}
                  onChange={(skills) => updateReq(idx, { preferredSkills: skills })}
                  placeholder="Add preferred skill…"
                />
              </div>
              <div>
                <label className="text-xs text-gray-400">Chapter filter (optional)<InfoTooltip content="Restrict suggestions to resources from a specific chapter/department." /></label>
                <input
                  type="text"
                  value={req.chapter ?? ""}
                  onChange={(e) =>
                    updateReq(idx, { chapter: e.target.value || undefined } as Partial<StaffingRequirement>)
                  }
                  placeholder="e.g. Art Direction"
                  className={INPUT_CLS}
                />
              </div>
            </div>
          </div>
        ))}
      </div>

      <button
        type="button"
        onClick={addReq}
        className="mt-3 flex items-center gap-1.5 text-sm text-brand-600 hover:text-brand-800 font-medium"
      >
        <span className="text-lg leading-none">+</span> Add Role
      </button>
    </div>
  );
}

// ─── Step 4: Suggestions ─────────────────────────────────────────────────────

// Matches StaffingSuggestion from @capakraken/shared (returned by staffing.getSuggestions)
type SuggestionItem = {
  resourceId: string;
  resourceName: string;
  eid: string;
  score: number;
  currentUtilization: number;
  availabilityConflicts: string[];
  estimatedDailyCostCents: number;
  valueScore?: number;
};

interface ReqSuggestionsProps {
  req: StaffingRequirement;
  startDate: string;
  endDate: string;
  assignments: Assignment[];
  onAssign: (resourceId: string, resourceName: string, role: string) => void;
  onUnassign: (resourceId: string) => void;
}

function ReqSuggestions({
  req,
  startDate,
  endDate,
  assignments,
  onAssign,
  onUnassign,
}: ReqSuggestionsProps) {
  const { canViewScores } = usePermissions();
  const { data, isLoading } = trpc.staffing.getSuggestions.useQuery(
    {
      requiredSkills: req.requiredSkills,
      preferredSkills: req.preferredSkills,
      startDate: new Date(startDate),
      endDate: new Date(endDate),
      hoursPerDay: req.hoursPerDay,
      ...(req.chapter ? { chapter: req.chapter } : {}),
    },
    {
      enabled: req.requiredSkills.length > 0 && !!startDate && !!endDate,
      staleTime: 30_000,
    },
  );

  const reqAssignments = assignments.filter((a) => a.requirementId === req.id);
  const assignedCount = reqAssignments.length;

  if (!req.requiredSkills.length) {
    return (
      <p className="text-xs text-amber-600">Add required skills in Step 3 to see suggestions.</p>
    );
  }

  if (isLoading) {
    return <p className="text-xs text-gray-400 animate-pulse">Loading suggestions…</p>;
  }

  const suggestions = (data ?? []) as unknown as SuggestionItem[];

  function availBadge(item: SuggestionItem) {
    if (item.availabilityConflicts.length === 0) {
      return (
        <span className="px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-100 text-green-700">
          Available
        </span>
      );
    }
    if (item.currentUtilization >= 100) {
      return (
        <span className="px-1.5 py-0.5 rounded text-[10px] font-medium bg-red-100 text-red-700">
          Unavailable
        </span>
      );
    }
    return (
      <span className="px-1.5 py-0.5 rounded text-[10px] font-medium bg-amber-100 text-amber-700">
        Partial
      </span>
    );
  }

  return (
    <div>
      <p className="text-xs text-gray-500 mb-2">
        Assigned: {assignedCount} / {req.headcount} needed
      </p>
      <div className="space-y-1.5 max-h-52 overflow-y-auto">
        {suggestions.length === 0 && (
          <p className="text-xs text-gray-400">No matching resources found.</p>
        )}
        {suggestions.slice(0, 10).map((item) => {
          const isAssigned = reqAssignments.some((a) => a.resourceId === item.resourceId);
          return (
            <div
              key={item.resourceId}
              className={clsx(
                "flex items-center gap-2 px-2.5 py-1.5 rounded-lg border text-xs",
                isAssigned
                  ? "border-brand-300 bg-brand-50"
                  : "border-gray-200 bg-white hover:border-gray-300",
              )}
            >
              <div className="flex-1 min-w-0">
                <div className="font-medium text-gray-800 truncate">{item.resourceName}</div>
                <div className="text-gray-400 font-mono">{item.eid}</div>
              </div>
              <div className="flex items-center gap-1.5 flex-shrink-0">
                <span className="px-1.5 py-0.5 rounded text-[10px] font-medium bg-gray-100 text-gray-600">
                  Score {item.score}
                </span>
                {canViewScores && item.valueScore != null && (
                  <span
                    className={`px-1.5 py-0.5 rounded text-[10px] font-medium ${
                      item.valueScore >= 70
                        ? "bg-green-100 text-green-700"
                        : item.valueScore >= 40
                        ? "bg-amber-100 text-amber-700"
                        : "bg-red-100 text-red-700"
                    }`}
                    title="Value Score (price/quality)"
                  >
                    ★ {item.valueScore}
                  </span>
                )}
                <span className="text-gray-400">{Math.round(item.currentUtilization)}%</span>
                {availBadge(item)}
              </div>
              {isAssigned ? (
                <button
                  type="button"
                  onClick={() => onUnassign(item.resourceId)}
                  className="flex-shrink-0 px-2 py-0.5 text-xs border border-brand-300 text-brand-700 rounded hover:bg-brand-100"
                >
                  Remove
                </button>
              ) : (
                <button
                  type="button"
                  onClick={() => onAssign(item.resourceId, item.resourceName, req.role)}
                  disabled={assignedCount >= req.headcount}
                  className="flex-shrink-0 px-2 py-0.5 text-xs bg-brand-600 text-white rounded hover:bg-brand-700 disabled:opacity-40"
                >
                  Assign
                </button>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

interface Step4Props {
  state: WizardState;
  onChange: (patch: Partial<WizardState>) => void;
}

function Step4({ state, onChange }: Step4Props) {
  function assign(requirementId: string, resourceId: string, resourceName: string, role: string) {
    onChange({
      assignments: [
        ...state.assignments,
        { requirementId, resourceId, resourceName, role },
      ],
    });
  }

  function unassign(requirementId: string, resourceId: string) {
    onChange({
      assignments: state.assignments.filter(
        (a) => !(a.requirementId === requirementId && a.resourceId === resourceId),
      ),
    });
  }

  if (state.staffingReqs.length === 0) {
    return (
      <p className="text-sm text-gray-400 text-center py-8">
        No staffing requirements defined. Go back to Step 3 to add some.
      </p>
    );
  }

  return (
    <div className="space-y-5 max-h-[55vh] overflow-y-auto pr-1">
      <p className="text-xs text-gray-500 flex items-center gap-1">
        AI-powered resource suggestions based on skills, availability, and utilization.
        <InfoTooltip content="Resources are ranked by skill match score, current utilization, and availability in the project period. Assign resources here or leave unfilled to create placeholder demands." />
      </p>
      {state.staffingReqs.map((req) => (
        <div key={req.id} className="border border-gray-200 rounded-lg p-3">
          <div className="flex items-center gap-2 mb-2">
            <span className="font-medium text-sm text-gray-800">{req.role || "Unnamed Role"}</span>
            <span className="text-xs text-gray-400">
              {req.hoursPerDay}h/day · {req.headcount} needed
            </span>
          </div>
          <ReqSuggestions
            req={req}
            startDate={state.startDate}
            endDate={state.endDate}
            assignments={state.assignments}
            onAssign={(resourceId, resourceName, role) =>
              assign(req.id, resourceId, resourceName, role)
            }
            onUnassign={(resourceId) => unassign(req.id, resourceId)}
          />
        </div>
      ))}
    </div>
  );
}

// ─── Step 5: Review & Create ─────────────────────────────────────────────────

interface Step5Props {
  state: WizardState;
  onChange: (patch: Partial<WizardState>) => void;
  onSubmit: () => void;
  isSubmitting: boolean;
  submitError: string | null;
}

function Step5({ state, onChange, onSubmit, isSubmitting, submitError }: Step5Props) {
  return (
    <div className="space-y-4">
      {/* Project summary */}
      <div className="flex items-center gap-1 mb-1">
        <p className="text-xs font-medium text-gray-600 uppercase tracking-wider">Project Summary</p>
        <InfoTooltip content="Review all project details before creation. The project, staffing demands, and any pre-assigned resources will be created together." />
      </div>
      <div className="bg-gray-50 rounded-lg p-4 text-sm space-y-2">
        <div className="grid grid-cols-2 gap-x-6 gap-y-1">
          <div>
            <span className="text-gray-500">Code:</span>{" "}
            <span className="font-mono font-medium">{state.shortCode || "—"}</span>
          </div>
          <div>
            <span className="text-gray-500">Name:</span>{" "}
            <span className="font-medium">{state.name || "—"}</span>
          </div>
          <div>
            <span className="text-gray-500">Type:</span> {state.orderType} / {state.allocationType}
          </div>
          <div>
            <span className="text-gray-500">Budget:</span>{" "}
            {state.budgetEur ? `€${parseFloat(state.budgetEur).toLocaleString()}` : "—"}
          </div>
          <div>
            <span className="text-gray-500">Dates:</span>{" "}
            {state.startDate} → {state.endDate}
          </div>
          <div>
            <span className="text-gray-500">Win %:</span> {state.winProbability}%
          </div>
          {state.responsiblePerson && (
            <div className="col-span-2">
              <span className="text-gray-500">Responsible:</span> {state.responsiblePerson}
            </div>
          )}
        </div>
      </div>

      {/* Staffing summary */}
      {state.staffingReqs.length > 0 && (
        <div>
          <p className="text-xs font-medium text-gray-600 uppercase tracking-wider mb-2">
            Staffing Requirements
          </p>
          <div className="space-y-1">
            {state.staffingReqs.map((req) => {
              const assigned = state.assignments.filter((a) => a.requirementId === req.id);
              return (
                <div
                  key={req.id}
                  className="flex items-center gap-2 text-sm px-3 py-1.5 rounded-lg bg-gray-50"
                >
                  <span className="flex-1 font-medium">{req.role || "Unnamed"}</span>
                  <span className="text-gray-400">{req.hoursPerDay}h/day</span>
                  <span
                    className={clsx(
                      "text-xs font-medium",
                      assigned.length >= req.headcount ? "text-green-600" : "text-amber-600",
                    )}
                  >
                    {assigned.length}/{req.headcount} assigned
                  </span>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* Draft toggle */}
      <div className="border border-gray-200 rounded-lg p-4">
        <p className="text-xs font-medium text-gray-600 uppercase tracking-wider mb-3 flex items-center gap-1">
          Save as
          <InfoTooltip content="Draft projects are hidden from the timeline until activated. Active projects appear on the timeline immediately." />
        </p>
        <div className="flex gap-4">
          <label className="flex items-center gap-2 cursor-pointer">
            <input
              type="radio"
              name="saveMode"
              checked={state.saveAsDraft}
              onChange={() => onChange({ saveAsDraft: true })}
              className="accent-brand-600"
            />
            <div>
              <div className="text-sm font-medium">Draft</div>
              <div className="text-xs text-gray-400">Hidden on timeline until enabled</div>
            </div>
          </label>
          <label className="flex items-center gap-2 cursor-pointer">
            <input
              type="radio"
              name="saveMode"
              checked={!state.saveAsDraft}
              onChange={() => onChange({ saveAsDraft: false })}
              className="accent-brand-600"
            />
            <div>
              <div className="text-sm font-medium">Active</div>
              <div className="text-xs text-gray-400">Visible on timeline immediately</div>
            </div>
          </label>
        </div>
      </div>

      {submitError && (
        <div className="px-3 py-2 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
          {submitError}
        </div>
      )}

      <div className="flex justify-end">
        <button
          type="button"
          onClick={onSubmit}
          disabled={isSubmitting}
          className={BTN_PRIMARY}
        >
          {isSubmitting
            ? "Creating…"
            : state.saveAsDraft
              ? "Create Draft Project"
              : "Create Active Project"}
        </button>
      </div>
    </div>
  );
}

// ─── Wizard shell ─────────────────────────────────────────────────────────────

interface ProjectWizardProps {
  open: boolean;
  onClose: () => void;
}

export function ProjectWizard({ open, onClose }: ProjectWizardProps) {
  const utils = trpc.useUtils();
  const [step, setStep] = useState(0);
  const [state, setState] = useState<WizardState>(makeDefaultState);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState<string | null>(null);
  const [showConfetti, setShowConfetti] = useState(false);
  const [showSuccessToast, setShowSuccessToast] = useState(false);

  const createProject = trpc.project.create.useMutation();
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const createAssignment = (trpc.allocation.createAssignment.useMutation as any)() as {
    mutateAsync: (input: unknown) => Promise<unknown>;
  };
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const createDemandRequirement = (trpc.allocation.createDemandRequirement.useMutation as any)() as {
    mutateAsync: (input: unknown) => Promise<unknown>;
  };

  const patch = useCallback((p: Partial<WizardState>) => {
    setState((prev) => ({ ...prev, ...p }));
  }, []);

  function reset() {
    setStep(0);
    setState(makeDefaultState());
    setIsSubmitting(false);
    setSubmitError(null);
  }

  function handleClose() {
    reset();
    onClose();
  }

  function canGoNext(): boolean {
    if (step === 0) {
      return (
        state.shortCode.trim().length > 0 &&
        /^[A-Z0-9_-]+$/.test(state.shortCode) &&
        state.name.trim().length > 0
      );
    }
    if (step === 1) {
      return (
        !!state.startDate &&
        !!state.endDate &&
        state.endDate >= state.startDate &&
        (state.budgetEur === "" || parseFloat(state.budgetEur) >= 0)
      );
    }
    return true;
  }

  async function handleSubmit() {
    setIsSubmitting(true);
    setSubmitError(null);

    try {
      const project = await createProject.mutateAsync({
        shortCode: state.shortCode.trim(),
        name: state.name.trim(),
        orderType: state.orderType as unknown as OrderType,
        allocationType: state.allocationType as unknown as AllocationType,
        winProbability: state.winProbability,
        budgetCents: state.budgetEur ? Math.round(parseFloat(state.budgetEur) * 100) : 0,
        startDate: new Date(state.startDate),
        endDate: new Date(state.endDate),
        staffingReqs: state.staffingReqs,
        status: state.saveAsDraft ? ProjectStatus.DRAFT : ProjectStatus.ACTIVE,
        responsiblePerson: state.responsiblePerson.trim(),
        blueprintId: state.blueprintId ?? undefined,
        dynamicFields: {},
      });

      // Create draft assignments for assigned resources
      for (const assignment of state.assignments) {
        try {
          const req = state.staffingReqs.find((r) => r.id === assignment.requirementId);
          const hoursPerDay = req?.hoursPerDay ?? 8;
          const percentage = Math.min(100, Math.round((hoursPerDay / 8) * 100));
          await createAssignment.mutateAsync({
            projectId: project.id,
            resourceId: assignment.resourceId,
            startDate: new Date(state.startDate),
            endDate: new Date(state.endDate),
            hoursPerDay,
            percentage,
            role: assignment.role,
            roleId: req?.roleId,
            status: AllocationStatus.PROPOSED,
            metadata: {},
          });
        } catch {
          // Non-fatal — skip duplicate allocations
        }
      }

      // Create open demand for unassigned slots
      for (const req of state.staffingReqs) {
        const assignedCount = state.assignments.filter((a) => a.requirementId === req.id).length;
        const unassigned = req.headcount - assignedCount;
        if (unassigned <= 0) continue;
        try {
          await createDemandRequirement.mutateAsync({
            projectId: project.id,
            startDate: new Date(state.startDate),
            endDate: new Date(state.endDate),
            hoursPerDay: req.hoursPerDay,
            percentage: Math.min(100, Math.round((req.hoursPerDay / 8) * 100)),
            role: req.role || undefined,
            roleId: req.roleId,
            headcount: unassigned,
            status: AllocationStatus.PROPOSED,
            metadata: {},
          });
        } catch {
          // Non-fatal
        }
      }

      await utils.project.list.invalidate();
      await utils.timeline.getEntries.invalidate();
      await utils.timeline.getEntriesView.invalidate();
      setShowConfetti(true);
      setShowSuccessToast(true);
      setTimeout(() => {
        setShowConfetti(false);
        handleClose();
      }, 1200);
    } catch (err) {
      let errorMessage = "Failed to create project";
      if (err instanceof Error) {
        try {
          const parsed: unknown = JSON.parse(err.message);
          if (Array.isArray(parsed) && parsed.length > 0) {
            errorMessage = (parsed as { message?: string }[])
              .map((e) => e.message)
              .filter(Boolean)
              .join("; ");
          } else {
            errorMessage = err.message;
          }
        } catch {
          errorMessage = err.message;
        }
      }
      setSubmitError(errorMessage);
    } finally {
      setIsSubmitting(false);
    }
  }

  if (!open) return null;

  function handleBackdropClick(e: React.MouseEvent<HTMLDivElement>) {
    if (e.target === e.currentTarget) handleClose();
  }

  return (
    <div
      className="fixed inset-0 bg-black/50 z-50 flex items-start justify-center overflow-y-auto py-8 px-4"
      onClick={handleBackdropClick}
    >
      <div className="bg-white rounded-xl shadow-2xl w-full max-w-2xl relative">
        {/* Celebration effects */}
        <ConfettiBurst trigger={showConfetti} />
        <SuccessToast
          show={showSuccessToast}
          message="Project created successfully!"
          onDone={() => setShowSuccessToast(false)}
        />
        {/* Header */}
        <div className="flex items-center justify-between px-6 py-4 border-b border-gray-200">
          <h2 className="text-lg font-semibold text-gray-900">New Project Wizard</h2>
          <button
            type="button"
            onClick={handleClose}
            className="text-gray-400 hover:text-gray-600 text-2xl leading-none"
            aria-label="Close"
          >
            ×
          </button>
        </div>

        {/* Body */}
        <div className="px-6 py-5">
          <StepBar current={step} />

          {step === 0 && <Step1 state={state} onChange={patch} />}
          {step === 1 && <Step2 state={state} onChange={patch} />}
          {step === 2 && <Step3 state={state} onChange={patch} />}
          {step === 3 && <Step4 state={state} onChange={patch} />}
          {step === 4 && (
            <Step5
              state={state}
              onChange={patch}
              onSubmit={handleSubmit}
              isSubmitting={isSubmitting}
              submitError={submitError}
            />
          )}
        </div>

        {/* Footer nav */}
        {step < 4 && (
          <div className="flex items-center justify-between px-6 py-4 border-t border-gray-200">
            <button
              type="button"
              onClick={step === 0 ? handleClose : () => setStep((s) => s - 1)}
              className={BTN_SECONDARY}
            >
              {step === 0 ? "Cancel" : "← Back"}
            </button>
            <button
              type="button"
              onClick={() => setStep((s) => s + 1)}
              disabled={!canGoNext()}
              className={BTN_PRIMARY}
            >
              {step === 3 ? "Review →" : "Next →"}
            </button>
          </div>
        )}
        {step === 4 && (
          <div className="flex items-center px-6 py-4 border-t border-gray-200">
            <button
              type="button"
              onClick={() => setStep((s) => s - 1)}
              className={BTN_SECONDARY}
            >
              ← Back
            </button>
          </div>
        )}
      </div>
    </div>
  );
}
