"use client";

import { useState, useCallback } from "react";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidatePlanningViews } from "~/hooks/useInvalidatePlanningViews.js";
import { AllocationStatus } from "@capakraken/shared";
import { DateInput } from "~/components/ui/DateInput.js";
import { SkillTagInput } from "~/components/ui/SkillTagInput.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
import { SuccessToast } from "~/components/ui/SuccessToast.js";
import { Button } from "@capakraken/ui";

interface SearchCriteria {
  startDate: string;
  endDate: string;
  hoursPerDay: number;
}

export function StaffingPanel() {
  const [requiredSkills, setRequiredSkills] = useState<string[]>(["TypeScript", "React"]);
  const [startDate, setStartDate] = useState(() => {
    const d = new Date();
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
  });
  const [endDate, setEndDate] = useState(() => {
    const d = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000);
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
  });
  const [hoursPerDay, setHoursPerDay] = useState(8);
  const [submitted, setSubmitted] = useState(false);
  const [assignedIds, setAssignedIds] = useState<Set<string>>(new Set());
  const [toast, setToast] = useState<{ show: boolean; message: string; variant: "success" | "warning" }>({
    show: false,
    message: "",
    variant: "success",
  });

  const clearToast = useCallback(() => setToast((t) => ({ ...t, show: false })), []);

  const { data: suggestions, isLoading } = trpc.staffing.getSuggestions.useQuery(
    {
      requiredSkills: requiredSkills,
      startDate: new Date(startDate),
      endDate: new Date(endDate),
      hoursPerDay,
    },
    { enabled: submitted },
  );

  const visibleSuggestions = suggestions?.filter((s) => !assignedIds.has(s.resourceId));

  const handleAssigned = useCallback((resourceId: string, resourceName: string) => {
    setAssignedIds((prev) => new Set(prev).add(resourceId));
    setToast({ show: true, message: `${resourceName} assigned successfully`, variant: "success" });
  }, []);

  const searchCriteria: SearchCriteria = { startDate, endDate, hoursPerDay };

  return (
    <div className="app-page space-y-6">
      <SuccessToast show={toast.show} message={toast.message} variant={toast.variant} onDone={clearToast} />

      <div className="app-page-header gap-4">
        <div className="space-y-3">
          <span className="inline-flex items-center rounded-full border border-brand-200 bg-brand-50 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.24em] text-brand-700 dark:border-brand-900/60 dark:bg-brand-900/30 dark:text-brand-200">
            Staffing
          </span>
          <div>
            <h1 className="app-page-title">Staffing Suggestions</h1>
            <p className="app-page-subtitle max-w-2xl">
              Match open work with the strongest available people based on skills, availability, utilization, and cost.
            </p>
          </div>
        </div>
        <div className="app-surface max-w-xl p-4">
          <p className="text-sm font-medium text-gray-900 dark:text-gray-100">How scoring works</p>
          <p className="mt-1 text-sm text-gray-500">
            CapaKraken blends skill fit, free capacity, cost, and current utilization. Add the must-have skills first, then narrow the date window to get cleaner results.
          </p>
        </div>
      </div>

      <div className="grid grid-cols-1 gap-6 xl:grid-cols-[360px_minmax(0,1fr)]">
        <div className="space-y-6">
          <div className="app-surface-strong p-6">
            <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Search Criteria</h2>
            <p className="mt-1 text-sm text-gray-500">Define the role needs and let the matching engine rank the best candidates.</p>

            <div className="mt-6 space-y-5">
              <div>
                <label className="app-label">Required Skills<InfoTooltip content="Skills the candidate must have. The engine scores overlap and proficiency against this list." /></label>
                <SkillTagInput
                  value={requiredSkills}
                  onChange={setRequiredSkills}
                  placeholder="Add skill..."
                />
              </div>

              <div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-1">
                <div>
                  <label className="app-label">Start Date</label>
                  <DateInput
                    value={startDate}
                    onChange={setStartDate}
                    className="app-input"
                  />
                </div>

                <div>
                  <label className="app-label">End Date</label>
                  <DateInput
                    value={endDate}
                    onChange={setEndDate}
                    min={startDate}
                    className="app-input"
                  />
                </div>
              </div>

              <div>
                <label className="app-label">Hours per Day<InfoTooltip content="Required hours per day for the role. Used to check conflicts and estimate capacity." /></label>
                <input
                  type="number"
                  value={hoursPerDay}
                  onChange={(e) => setHoursPerDay(Number(e.target.value))}
                  min={1}
                  max={24}
                  className="app-input"
                />
              </div>

              <button
                onClick={() => setSubmitted(true)}
                className="inline-flex w-full items-center justify-center rounded-xl bg-brand-600 px-4 py-3 text-sm font-semibold text-white transition hover:bg-brand-700"
              >
                Find Matches
              </button>
            </div>
          </div>

          <div className="app-surface p-5">
            <div className="grid grid-cols-3 gap-3 text-sm">
              <div>
                <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Skills</div>
                <div className="mt-1 text-gray-600 dark:text-gray-300">Quality of skill overlap with the requested stack.</div>
              </div>
              <div>
                <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Availability</div>
                <div className="mt-1 text-gray-600 dark:text-gray-300">Conflicts and free capacity during the selected period.</div>
              </div>
              <div>
                <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Cost + Load</div>
                <div className="mt-1 text-gray-600 dark:text-gray-300">Cost efficiency and current utilization weighting.</div>
              </div>
            </div>
          </div>
        </div>

        <div className="space-y-4">
          {isLoading && (
            <div className="app-surface-strong py-16 text-center text-sm text-gray-500">
              Finding best matches...
            </div>
          )}

          {visibleSuggestions && visibleSuggestions.length === 0 && (
            <div className="app-surface-strong py-16 text-center text-sm text-gray-500">
              {assignedIds.size > 0 ? "All suggestions have been assigned." : "No resources found matching your criteria."}
            </div>
          )}

          {visibleSuggestions && visibleSuggestions.length > 0 && (
            <div className="space-y-4">
              {visibleSuggestions.map((suggestion, idx) => (
                <SuggestionCard
                  key={suggestion.resourceId}
                  suggestion={suggestion}
                  rank={idx + 1}
                  searchCriteria={searchCriteria}
                  onAssigned={handleAssigned}
                  onError={(msg) => setToast({ show: true, message: msg, variant: "warning" })}
                />
              ))}
            </div>
          )}

          {!submitted && (
            <div className="app-surface-strong border-dashed py-20 text-center">
              <div className="mx-auto max-w-md">
                <div className="text-sm font-medium text-gray-900 dark:text-gray-100">No suggestions yet</div>
                <p className="mt-2 text-sm text-gray-500">
                  Add the required skills and date range, then run the search to see ranked staffing matches.
                </p>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* -------------------------------------------------------------------------- */
/*  Suggestion Card                                                           */
/* -------------------------------------------------------------------------- */

interface SuggestionLike {
  resourceId: string;
  resourceName: string;
  eid: string;
  score: number;
  valueScore?: number;
  scoreBreakdown: {
    skillScore: number;
    availabilityScore: number;
    costScore: number;
    utilizationScore: number;
    total?: number;
  };
  matchedSkills: string[];
  missingSkills: string[];
  availabilityConflicts: string[];
  estimatedDailyCostCents: number;
  currentUtilization: number;
  remainingHours?: number;
  remainingHoursPerDay?: number;
  baseAvailableHours?: number;
  effectiveAvailableHours?: number;
  holidayHoursDeduction?: number;
  location?: {
    countryCode: string | null;
    countryName: string | null;
    federalState: string | null;
    metroCityName: string | null;
    label: string;
  };
  capacity?: {
    requestedHoursPerDay: number;
    requestedHoursTotal: number;
    baseWorkingDays: number;
    effectiveWorkingDays: number;
    baseAvailableHours: number;
    effectiveAvailableHours: number;
    bookedHours: number;
    remainingHours: number;
    remainingHoursPerDay: number;
    holidayCount: number;
    holidayWorkdayCount: number;
    holidayHoursDeduction: number;
    absenceDayEquivalent: number;
    absenceHoursDeduction: number;
  };
  conflicts?: {
    count: number;
    conflictDays: string[];
    details: Array<{
      date: string;
      baseHours: number;
      effectiveHours: number;
      allocatedHours: number;
      remainingHours: number;
      requestedHours: number;
      shortageHours: number;
      absenceFraction: number;
      isHoliday: boolean;
    }>;
  };
  ranking?: {
    rank: number;
    baseRank: number;
    tieBreakerApplied: boolean;
    tieBreakerReason: string | null;
    model: string;
    components: Array<{
      key: string;
      label: string;
      score: number;
    }>;
  };
}

interface SuggestionCardProps {
  suggestion: SuggestionLike;
  rank: number;
  searchCriteria: SearchCriteria;
  onAssigned: (resourceId: string, resourceName: string) => void;
  onError: (message: string) => void;
}

function SuggestionCard({ suggestion, rank, searchCriteria, onAssigned, onError }: SuggestionCardProps) {
  const [showDetails, setShowDetails] = useState(false);
  const [showAssignForm, setShowAssignForm] = useState(false);
  const locationLabel = suggestion.location?.label
    || [suggestion.location?.countryCode, suggestion.location?.federalState, suggestion.location?.metroCityName]
      .filter(Boolean)
      .join(" / ")
    || "No location";
  const capacity = suggestion.capacity;
  const conflicts = suggestion.conflicts;
  const conflictCount = conflicts?.count ?? suggestion.availabilityConflicts.length;
  const remainingHours = capacity?.remainingHours ?? suggestion.remainingHours ?? 0;
  const remainingHoursPerDay = capacity?.remainingHoursPerDay ?? suggestion.remainingHoursPerDay ?? 0;
  const baseAvailableHours = capacity?.baseAvailableHours ?? suggestion.baseAvailableHours ?? 0;
  const effectiveAvailableHours = capacity?.effectiveAvailableHours ?? suggestion.effectiveAvailableHours ?? 0;
  const holidayHoursDeduction = capacity?.holidayHoursDeduction ?? suggestion.holidayHoursDeduction ?? 0;

  return (
    <div data-suggestion className="app-surface p-5">
      <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
        <div className="flex items-center gap-4">
          <div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-brand-100 font-display text-lg font-semibold text-brand-700 dark:bg-brand-900/40 dark:text-brand-200">
            {rank}
          </div>
          <div>
            <div className="text-base font-semibold text-gray-900 dark:text-gray-100">{suggestion.resourceName}</div>
            <div className="text-sm text-gray-500">{suggestion.eid}</div>
            <div className="mt-1 text-xs text-gray-500">{locationLabel}</div>
          </div>
        </div>
        <div className="flex items-start gap-3">
          <Button
            variant="ghost"
            size="sm"
            onClick={() => setShowDetails((prev) => !prev)}
          >
            {showDetails ? "Hide Details" : "Details"}
          </Button>
          <Button
            variant="primary"
            size="sm"
            onClick={() => setShowAssignForm((prev) => !prev)}
          >
            {showAssignForm ? "Close Assign" : "Assign"}
          </Button>
          <div className="rounded-2xl border border-brand-200 bg-brand-50 px-4 py-3 text-right dark:border-brand-900/50 dark:bg-brand-900/20">
            <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-brand-700 dark:text-brand-200 inline-flex items-center gap-0.5">Match Score<InfoTooltip content="Composite score (0-100) blending skill fit, free capacity, cost efficiency, and current utilization." /></div>
            <div className="mt-1 text-3xl font-semibold text-brand-700 dark:text-brand-100">{suggestion.score}</div>
          </div>
        </div>
      </div>

      <div className="mt-4 flex flex-wrap gap-2">
        {suggestion.matchedSkills.map((skill) => (
          <span key={skill} className="rounded-full bg-green-50 px-2.5 py-1 text-xs font-medium text-green-700 dark:bg-green-950/30 dark:text-green-300">
            {skill}
          </span>
        ))}
        {suggestion.missingSkills.map((skill) => (
          <span key={skill} className="rounded-full bg-red-50 px-2.5 py-1 text-xs font-medium text-red-600 dark:bg-red-950/30 dark:text-red-300">
            {skill} missing
          </span>
        ))}
      </div>

      <div className="mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
        <StatCard
          label="Free / Workday"
          value={formatHours(remainingHoursPerDay)}
          tone={remainingHoursPerDay >= searchCriteria.hoursPerDay ? "good" : "warn"}
          helper={`${formatHours(remainingHours)} total in window`}
        />
        <StatCard
          label="Capacity"
          value={`${formatHours(effectiveAvailableHours)} effective`}
          helper={`${formatHours(baseAvailableHours)} base`}
        />
        <StatCard
          label="Holiday Deduction"
          value={holidayHoursDeduction > 0 ? formatHours(holidayHoursDeduction) : "0h"}
          tone={holidayHoursDeduction > 0 ? "warn" : "neutral"}
          helper={capacity ? `${capacity.holidayWorkdayCount} affected workdays` : "No local holiday impact"}
        />
        <StatCard
          label="Conflicts"
          value={String(conflictCount)}
          tone={conflictCount > 0 ? "warn" : "good"}
          helper={conflictCount > 0 ? `${conflictCount} overloaded day${conflictCount === 1 ? "" : "s"}` : "No day-level overloads"}
        />
      </div>

      <div className="mt-4 flex flex-wrap items-center gap-4 text-xs text-gray-500">
        <span>LCR: {(suggestion.estimatedDailyCostCents / 100 / 8).toFixed(0)} EUR/h</span>
        <span>Utilization: {Math.round(suggestion.currentUtilization)}%</span>
        {suggestion.valueScore != null && (
          <span>Value Score: {suggestion.valueScore}</span>
        )}
        {conflictCount > 0 && (
          <span className="font-medium text-amber-600 dark:text-amber-300">
            {conflictCount} scheduling conflict{conflictCount === 1 ? "" : "s"}
          </span>
        )}
      </div>

      {showDetails && (
        <div className="mt-5 space-y-4 rounded-2xl border border-gray-200 bg-gray-50/80 p-4 dark:border-gray-800 dark:bg-gray-950/40">
          <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
            <ScoreBar label="Skills" value={suggestion.scoreBreakdown.skillScore} tooltip="Quality of skill overlap with the requested stack, weighted by proficiency level." />
            <ScoreBar label="Availability" value={suggestion.scoreBreakdown.availabilityScore} tooltip="Free capacity during the selected period, accounting for existing bookings and vacations." />
            <ScoreBar label="Cost" value={suggestion.scoreBreakdown.costScore} tooltip="Cost efficiency based on the resource's LCR relative to the team average." />
            <ScoreBar label="Utilization" value={suggestion.scoreBreakdown.utilizationScore} tooltip="Current workload. Higher score means more capacity available (lower utilization)." />
          </div>

          <div className="grid gap-4 xl:grid-cols-[1.15fr_1fr]">
            <div className="rounded-2xl border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-gray-950/60">
              <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500">Capacity Basis</div>
              <div className="mt-3 grid gap-3 sm:grid-cols-2">
                <MetricLine label="Requested load" value={`${formatHours(capacity?.requestedHoursPerDay ?? searchCriteria.hoursPerDay)} / day`} />
                <MetricLine label="Requested total" value={formatHours(capacity?.requestedHoursTotal ?? 0)} />
                <MetricLine label="Base working days" value={String(capacity?.baseWorkingDays ?? 0)} />
                <MetricLine label="Effective working days" value={String(capacity?.effectiveWorkingDays ?? 0)} />
                <MetricLine label="Base available hours" value={formatHours(baseAvailableHours)} />
                <MetricLine label="Effective available hours" value={formatHours(effectiveAvailableHours)} />
                <MetricLine label="Booked hours" value={formatHours(capacity?.bookedHours ?? 0)} />
                <MetricLine label="Remaining hours" value={formatHours(remainingHours)} />
                <MetricLine label="Holiday deduction" value={formatHours(holidayHoursDeduction)} />
                <MetricLine label="Absence deduction" value={formatHours(capacity?.absenceHoursDeduction ?? 0)} />
              </div>
            </div>

            <div className="space-y-4">
              <div className="rounded-2xl border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-gray-950/60">
                <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500">Ranking Basis</div>
                <p className="mt-2 text-sm text-gray-600 dark:text-gray-300">
                  {suggestion.ranking?.model ?? "Composite ranking across skill fit, availability, cost, and utilization."}
                </p>
                <div className="mt-3 space-y-2 text-sm text-gray-600 dark:text-gray-300">
                  {(suggestion.ranking?.components ?? []).map((component) => (
                    <MetricLine key={component.key} label={component.label} value={`${component.score}`} />
                  ))}
                  {suggestion.ranking?.tieBreakerReason && (
                    <div className="rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-200">
                      {suggestion.ranking.tieBreakerReason}
                    </div>
                  )}
                </div>
              </div>

              <div className="rounded-2xl border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-gray-950/60">
                <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500">Location + Calendar</div>
                <div className="mt-3 space-y-2 text-sm text-gray-600 dark:text-gray-300">
                  <MetricLine label="Location" value={locationLabel} />
                  <MetricLine label="Holiday dates" value={String(capacity?.holidayCount ?? 0)} />
                  <MetricLine label="Holiday workdays" value={String(capacity?.holidayWorkdayCount ?? 0)} />
                  <MetricLine label="Absence days" value={String(capacity?.absenceDayEquivalent ?? 0)} />
                </div>
              </div>
            </div>
          </div>

          <div className="rounded-2xl border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-gray-950/60">
            <div className="flex items-center justify-between gap-3">
              <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500">Conflict Check</div>
              <div className="text-xs text-gray-500">
                Requested {formatHours(searchCriteria.hoursPerDay)} / day between {searchCriteria.startDate} and {searchCriteria.endDate}
              </div>
            </div>
            {conflictCount === 0 ? (
              <p className="mt-3 text-sm text-green-700 dark:text-green-300">
                No overloaded working days in the selected window.
              </p>
            ) : (
              <div className="mt-3 space-y-2">
                {(conflicts?.details ?? []).slice(0, 6).map((item) => (
                  <div key={item.date} className="rounded-xl border border-amber-200 bg-amber-50/80 px-3 py-2 text-sm text-amber-900 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-100">
                    <div className="flex flex-wrap items-center justify-between gap-2">
                      <span className="font-medium">{item.date}</span>
                      <span>Short by {formatHours(item.shortageHours)}</span>
                    </div>
                    <div className="mt-1 text-xs">
                      Base {formatHours(item.baseHours)} | Effective {formatHours(item.effectiveHours)} | Already booked {formatHours(item.allocatedHours)} | Remaining {formatHours(item.remainingHours)}
                    </div>
                  </div>
                ))}
                {conflictCount > 6 && (
                  <p className="text-xs text-gray-500">
                    +{conflictCount - 6} more conflict day{conflictCount - 6 === 1 ? "" : "s"}
                  </p>
                )}
              </div>
            )}
          </div>
        </div>
      )}

      {showAssignForm && (
        <AssignForm
          resourceId={suggestion.resourceId}
          resourceName={suggestion.resourceName}
          searchCriteria={searchCriteria}
          onAssigned={() => onAssigned(suggestion.resourceId, suggestion.resourceName)}
          onError={onError}
          onCancel={() => setShowAssignForm(false)}
        />
      )}
    </div>
  );
}

/* -------------------------------------------------------------------------- */
/*  Inline Assign Form                                                        */
/* -------------------------------------------------------------------------- */

interface AssignFormProps {
  resourceId: string;
  resourceName: string;
  searchCriteria: SearchCriteria;
  onAssigned: () => void;
  onError: (message: string) => void;
  onCancel: () => void;
}

function AssignForm({ resourceId, resourceName, searchCriteria, onAssigned, onError, onCancel }: AssignFormProps) {
  const [projectId, setProjectId] = useState("");
  const [assignStartDate, setAssignStartDate] = useState(searchCriteria.startDate);
  const [assignEndDate, setAssignEndDate] = useState(searchCriteria.endDate);
  const [assignHours, setAssignHours] = useState(searchCriteria.hoursPerDay);
  const [roleId, setRoleId] = useState("");
  const [roleFreeText, setRoleFreeText] = useState("");

  const invalidatePlanningViews = useInvalidatePlanningViews();

  const { data: projects } = trpc.project.list.useQuery(
    { limit: 500 },
    { staleTime: 60_000 },
  );
  const { data: roles } = trpc.role.list.useQuery(
    { isActive: true },
    { staleTime: 60_000 },
  );

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const createAssignment = (trpc.allocation.createAssignment.useMutation as any)({
    onSuccess: () => {
      invalidatePlanningViews();
      onAssigned();
    },
    onError: (err: { message: string }) => {
      onError(err.message || "Failed to create assignment");
    },
  }) as { mutate: (input: unknown) => void; isPending: boolean };

  const canSubmit = projectId && assignStartDate && assignEndDate && assignHours > 0;

  const handleSubmit = () => {
    if (!canSubmit) return;
    createAssignment.mutate({
      resourceId,
      projectId,
      startDate: new Date(assignStartDate),
      endDate: new Date(assignEndDate),
      hoursPerDay: assignHours,
      percentage: 100,
      ...(roleId ? { roleId } : {}),
      ...(roleFreeText ? { role: roleFreeText } : {}),
      status: AllocationStatus.PROPOSED,
      metadata: {},
    });
  };

  const projectList = (projects as { projects?: Array<{ id: string; name: string; shortCode?: string | null }> } | undefined)?.projects ?? [];
  const rolesList = (roles ?? []) as Array<{ id: string; name: string }>;
  const selectedProject = projectList.find((p) => p.id === projectId);

  return (
    <div className="mt-4 rounded-xl border border-brand-200 bg-brand-50/50 p-4 dark:border-brand-900/40 dark:bg-brand-950/30">
      <div className="mb-3 flex items-center justify-between">
        <h4 className="text-sm font-semibold text-gray-900 dark:text-gray-100">
          Assign {resourceName}
        </h4>
      </div>

      <div className="grid gap-3 sm:grid-cols-2">
        <div className="sm:col-span-2">
          <label className="app-label">Project *</label>
          <select
            value={projectId}
            onChange={(e) => setProjectId(e.target.value)}
            className="app-input"
          >
            <option value="">Select project...</option>
            {projectList.map((p) => (
              <option key={p.id} value={p.id}>
                {p.shortCode ? `[${p.shortCode}] ` : ""}{p.name}
              </option>
            ))}
          </select>
        </div>

        <div>
          <label className="app-label">Start Date</label>
          <DateInput
            value={assignStartDate}
            onChange={setAssignStartDate}
            className="app-input"
          />
        </div>

        <div>
          <label className="app-label">End Date</label>
          <DateInput
            value={assignEndDate}
            onChange={setAssignEndDate}
            min={assignStartDate}
            className="app-input"
          />
        </div>

        <div>
          <label className="app-label">Hours / Day</label>
          <input
            type="number"
            value={assignHours}
            onChange={(e) => setAssignHours(Number(e.target.value))}
            min={0.5}
            max={24}
            step={0.5}
            className="app-input"
          />
        </div>

        <div>
          <label className="app-label">Role</label>
          {rolesList.length > 0 ? (
            <select
              value={roleId}
              onChange={(e) => setRoleId(e.target.value)}
              className="app-input"
            >
              <option value="">No role</option>
              {rolesList.map((r) => (
                <option key={r.id} value={r.id}>
                  {r.name}
                </option>
              ))}
            </select>
          ) : (
            <input
              type="text"
              value={roleFreeText}
              onChange={(e) => setRoleFreeText(e.target.value)}
              placeholder="e.g. 3D Artist"
              className="app-input"
            />
          )}
        </div>
      </div>

      {selectedProject && (
        <div className="mt-3 text-xs text-gray-500">
          Assigning to <span className="font-medium text-gray-700 dark:text-gray-300">{selectedProject.name}</span> from {assignStartDate} to {assignEndDate} at {assignHours}h/day
        </div>
      )}

      <div className="mt-4 flex items-center gap-2">
        <Button
          variant="primary"
          size="sm"
          disabled={!canSubmit || createAssignment.isPending}
          onClick={handleSubmit}
        >
          {createAssignment.isPending ? "Assigning..." : "Confirm Assignment"}
        </Button>
        <Button
          variant="ghost"
          size="sm"
          onClick={onCancel}
          disabled={createAssignment.isPending}
        >
          Cancel
        </Button>
      </div>
    </div>
  );
}

/* -------------------------------------------------------------------------- */
/*  Score Bar                                                                 */
/* -------------------------------------------------------------------------- */

function ScoreBar({ label, value, tooltip }: { label: string; value: number; tooltip?: string }) {
  return (
    <div className="rounded-2xl border border-gray-200 bg-gray-50/70 p-3 dark:border-gray-700 dark:bg-gray-900/40">
      <div className="mb-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500 inline-flex items-center gap-0.5">{label}{tooltip && <InfoTooltip content={tooltip} />}</div>
      <div className="h-2 rounded-full bg-gray-200/80 dark:bg-gray-800">
        <div
          className="h-full rounded-full bg-brand-500"
          style={{ width: `${value}%` }}
        />
      </div>
      <div className="mt-2 text-sm font-medium text-gray-700 dark:text-gray-200">{value}</div>
    </div>
  );
}

function formatHours(value: number): string {
  const rounded = Math.round(value * 10) / 10;
  return `${rounded}h`;
}

function MetricLine({ label, value }: { label: string; value: string }) {
  return (
    <div className="flex items-center justify-between gap-3 border-b border-gray-100 pb-2 text-sm last:border-b-0 last:pb-0 dark:border-gray-800">
      <span className="text-gray-500 dark:text-gray-400">{label}</span>
      <span className="text-right font-medium text-gray-900 dark:text-gray-100">{value}</span>
    </div>
  );
}

function StatCard({
  label,
  value,
  helper,
  tone = "neutral",
}: {
  label: string;
  value: string;
  helper?: string;
  tone?: "neutral" | "good" | "warn";
}) {
  const toneClass = tone === "good"
    ? "border-green-200 bg-green-50/70 dark:border-green-900/40 dark:bg-green-950/20"
    : tone === "warn"
      ? "border-amber-200 bg-amber-50/70 dark:border-amber-900/40 dark:bg-amber-950/20"
      : "border-gray-200 bg-gray-50/70 dark:border-gray-700 dark:bg-gray-900/40";

  return (
    <div className={`rounded-2xl border p-3 ${toneClass}`}>
      <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">{label}</div>
      <div className="mt-2 text-lg font-semibold text-gray-900 dark:text-gray-100">{value}</div>
      {helper && (
        <div className="mt-1 text-xs text-gray-500 dark:text-gray-400">{helper}</div>
      )}
    </div>
  );
}
