"use client";

import { clsx } from "clsx";
import { useEffect, useState } from "react";
import { AllocationStatus, type StaffingRequirement } from "@capakraken/shared";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidateTimeline } from "~/hooks/useInvalidatePlanningViews.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { DateInput } from "~/components/ui/DateInput.js";

interface ProjectPanelProps {
  projectId: string;
  onClose: () => void;
}

interface DemandSummary {
  id: string;
  role: string;
  hoursPerDay: number;
  requestedHeadcount: number;
}

interface ProjectPanelAssignment {
  id: string;
  entityId?: string;
  sourceAllocationId?: string;
  resourceId: string;
  role: string | null;
  startDate: Date | string;
  endDate: Date | string;
  hoursPerDay: number;
  metadata: { includeSaturday?: boolean } | null;
  resource?: {
    displayName: string;
    eid: string;
  } | null;
}

interface ProjectPanelDemand {
  id: string;
  entityId?: string;
  sourceAllocationId?: string;
  role: string | null;
  hoursPerDay: number;
  requestedHeadcount: number;
  roleEntity?: {
    name: string;
  } | null;
}

interface ProjectPanelProject {
  name: string;
  orderType?: string;
  status?: string;
  startDate: Date | string;
  endDate: Date | string;
  budgetCents: number;
  staffingReqs?: unknown;
}

interface ProjectPanelContext {
  project: ProjectPanelProject;
  assignments?: ProjectPanelAssignment[];
  demands?: ProjectPanelDemand[];
}

interface ProjectPanelResource {
  id: string;
  displayName: string;
  eid: string;
  chapter?: string | null;
}

const STATUS_COLORS = {
  green: "bg-green-500",
  amber: "bg-amber-400",
  red: "bg-red-500",
};

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

function normalizeRole(value: string | null | undefined): string {
  return (value ?? "").trim().toLowerCase();
}

export function ProjectPanel({ projectId, onClose }: ProjectPanelProps) {
  const invalidateTimeline = useInvalidateTimeline();

  const { data: ctx, isLoading } = trpc.timeline.getProjectContext.useQuery(
    { projectId },
    { staleTime: 5_000 },
  );

  const { data: budgetStatus } = trpc.timeline.getBudgetStatus.useQuery(
    { projectId },
    { staleTime: 5_000 },
  );

  const updateMutation = trpc.timeline.updateAllocationInline.useMutation({
    onSuccess: invalidateTimeline,
  });

  const deleteMutation = trpc.allocation.deleteAssignment.useMutation({
    onSuccess: invalidateTimeline,
  });

  const createAssignmentMutation = trpc.allocation.createAssignment.useMutation({
    onSuccess: () => {
      invalidateTimeline();
      setAddingMember(false);
      setResourceSearch("");
    },
  });

  const [addingMember, setAddingMember] = useState(false);
  const [resourceSearch, setResourceSearch] = useState("");
  const [pendingEdits, setPendingEdits] = useState<
    Record<string, { hoursPerDay?: number; startDate?: string; endDate?: string; includeSaturday?: boolean; role?: string }>
  >({});
  const [confirmDelete, setConfirmDelete] = useState<string | null>(null);

  const { data: allResources } = trpc.resource.directory.useQuery(
    { search: resourceSearch },
    { enabled: addingMember, staleTime: 10_000 },
  );

  if (isLoading || !ctx) {
    return (
      <PanelShell onClose={onClose}>
        <div className="flex items-center justify-center h-64 text-gray-400 text-sm">Loading…</div>
      </PanelShell>
    );
  }

  const { project, assignments = [], demands = [] } = ctx as unknown as ProjectPanelContext;
  const staffingReqs = (project.staffingReqs as unknown as StaffingRequirement[]) ?? [];
  const effectiveAssignments = assignments as unknown as ProjectPanelAssignment[];
  const projectDemands = demands as unknown as ProjectPanelDemand[];
  const effectiveDemands: DemandSummary[] = projectDemands.length > 0
    ? projectDemands.map((demand) => ({
        id: demand.id,
        role: demand.roleEntity?.name ?? demand.role ?? "Unassigned",
        hoursPerDay: demand.hoursPerDay,
        requestedHeadcount: demand.requestedHeadcount,
      }))
    : staffingReqs.map((req, index) => ({
        id: `staffing-${index}`,
        role: req.role,
        hoursPerDay: req.hoursPerDay,
        requestedHeadcount: req.headcount,
      }));

  // Demand vs supply matching
  const reqMatches = effectiveDemands.map((demand) => {
    const demandRole = normalizeRole(demand.role);
    const matched = effectiveAssignments.filter((assignment) =>
      normalizeRole(assignment.role).includes(demandRole),
    );
    const totalHeadcount = matched.length;
    const fulfilled = totalHeadcount >= demand.requestedHeadcount;
    const partial = !fulfilled && totalHeadcount > 0;
    return { demand, matched, fulfilled, partial };
  });

  const unmatchedAssignments = effectiveAssignments.filter(
    (assignment) =>
      !effectiveDemands.some((demand) =>
        normalizeRole(assignment.role).includes(normalizeRole(demand.role)),
      ),
  );

  // Budget bar
  const budgetEUR = (project.budgetCents / 100).toFixed(0);
  const allocatedEUR = budgetStatus ? (budgetStatus.allocatedCents / 100).toFixed(0) : "—";
  const utilPct = budgetStatus?.utilizationPercent ?? 0;
  const budgetBarColor =
    utilPct >= 100 ? STATUS_COLORS.red : utilPct >= 85 ? STATUS_COLORS.amber : STATUS_COLORS.green;

  function getEdit(id: string) {
    return pendingEdits[id] ?? {};
  }

  function setEdit(id: string, patch: typeof pendingEdits[string]) {
    setPendingEdits((prev) => ({ ...prev, [id]: { ...(prev[id] ?? {}), ...patch } }));
  }

  function saveEdit(allocId: string) {
    const edit = getEdit(allocId);
    const alloc = effectiveAssignments.find((a) => a.id === allocId);
    if (!alloc) return;
    updateMutation.mutate({
      allocationId: getPlanningEntryMutationId(alloc),
      hoursPerDay: edit.hoursPerDay,
      startDate: edit.startDate ? new Date(edit.startDate) : undefined,
      endDate: edit.endDate ? new Date(edit.endDate) : undefined,
      includeSaturday: edit.includeSaturday,
      role: edit.role,
    });
    setPendingEdits((prev) => {
      const next = { ...prev };
      delete next[allocId];
      return next;
    });
  }

  function handleAddMember(resourceId: string) {
    createAssignmentMutation.mutate({
      resourceId,
      projectId,
      startDate: new Date(project.startDate),
      endDate: new Date(project.endDate),
      hoursPerDay: 8,
      percentage: 100,
      role: "Team Member",
      status: AllocationStatus.PROPOSED,
      metadata: {},
    });
  }

  const availableResources = (allResources?.resources ?? []) as unknown as ProjectPanelResource[];
  const filteredResources = availableResources.filter(
    (r) => !effectiveAssignments.some((a) => a.resourceId === r.id),
  );

  return (
    <PanelShell onClose={onClose}>
      {/* Header */}
      <div className="px-5 py-4 border-b border-gray-100">
        <div className="flex items-start justify-between gap-2">
          <div>
            <h2 className="text-base font-semibold text-gray-900">{project.name}</h2>
          </div>
          <div className="flex items-center gap-2 flex-shrink-0">
            <span className={clsx(
              "px-2 py-0.5 rounded-full text-xs font-medium",
              project.orderType === "CHARGEABLE" ? "bg-emerald-100 text-emerald-700" :
              project.orderType === "BD" ? "bg-violet-100 text-violet-700" :
              project.orderType === "INTERNAL" ? "bg-blue-100 text-blue-700" :
              "bg-gray-100 text-gray-600",
            )}>
              {project.orderType}
            </span>
            <span className="text-xs text-gray-400">{project.status}</span>
          </div>
        </div>
        <div className="text-xs text-gray-400 mt-1">
          {toDateInput(project.startDate)} → {toDateInput(project.endDate)}
        </div>
      </div>

      <div className="overflow-y-auto flex-1 px-5 py-4 space-y-6">

        {/* Budget section */}
        <section>
          <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Budget</h3>
          <div className="bg-gray-50 rounded-xl p-3 space-y-2">
            <div className="flex justify-between text-sm">
              <span className="text-gray-600">Allocated</span>
              <span className="font-semibold text-gray-900">€{allocatedEUR} / €{budgetEUR}</span>
            </div>
            <div className="w-full h-2 bg-gray-200 rounded-full overflow-hidden">
              <div
                className={clsx("h-full rounded-full transition-all", budgetBarColor)}
                style={{ width: `${Math.min(utilPct, 100)}%` }}
              />
            </div>
            <div className="flex justify-between text-xs text-gray-400">
              <span>{utilPct.toFixed(1)}% utilized</span>
              {budgetStatus && (
                <span>€{(budgetStatus.remainingCents / 100).toFixed(0)} remaining</span>
              )}
            </div>
            {budgetStatus?.warnings.map((w, i) => (
              <div
                key={i}
                className={clsx(
                  "text-xs px-2 py-1 rounded-lg",
                  w.level === "critical" ? "bg-red-50 text-red-700" :
                  w.level === "warning" ? "bg-amber-50 text-amber-700" :
                  "bg-blue-50 text-blue-700",
                )}
              >
                {w.message}
              </div>
            ))}
          </div>
        </section>

        {/* Demand vs Supply */}
        {effectiveDemands.length > 0 && (
          <section>
            <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">
              Demand vs Supply
            </h3>
            <div className="space-y-2">
              {reqMatches.map(({ demand, matched, fulfilled, partial }) => (
                <div key={demand.id} className="border border-gray-100 rounded-xl overflow-hidden">
                  <div className={clsx(
                    "flex items-center justify-between px-3 py-2",
                    fulfilled ? "bg-green-50" : partial ? "bg-amber-50" : "bg-red-50",
                  )}>
                    <div>
                      <span className="text-sm font-medium text-gray-800">{demand.role}</span>
                      <span className="ml-2 text-xs text-gray-500">{demand.requestedHeadcount} needed · {demand.hoursPerDay}h/day</span>
                    </div>
                    <span className={clsx(
                      "text-xs font-semibold",
                      fulfilled ? "text-green-600" : partial ? "text-amber-600" : "text-red-600",
                    )}>
                      {fulfilled ? "✓ Filled" : partial ? `${matched.length}/${demand.requestedHeadcount}` : "Unfilled"}
                    </span>
                  </div>
                  {matched.length > 0 && (
                    <div className="px-3 py-1.5 bg-white border-t border-gray-100 space-y-0.5">
                      {matched.map((a) => (
                        <div key={a.id} className="flex items-center justify-between text-xs text-gray-600">
                          <span>{a.resource?.displayName}</span>
                          <span className="text-gray-400">{a.hoursPerDay}h/day</span>
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              ))}
            </div>
            {unmatchedAssignments.length > 0 && (
              <div className="mt-2">
                <div className="text-xs text-gray-400 mb-1">Unmatched assignments</div>
                {unmatchedAssignments.map((a) => (
                  <div key={a.id} className="text-xs text-gray-500 flex justify-between">
                    <span>{a.resource?.displayName} — {a.role}</span>
                    <span>{a.hoursPerDay}h/day</span>
                  </div>
                ))}
              </div>
            )}
          </section>
        )}

        {/* Team */}
        <section>
          <div className="flex items-center justify-between mb-2">
            <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider">Team</h3>
            <button
              onClick={() => setAddingMember(true)}
              className="text-xs text-brand-600 hover:text-brand-800 font-medium"
            >
              + Add Member
            </button>
          </div>

          {/* Resource search for adding */}
          {addingMember && (
            <div className="mb-3 border border-gray-200 rounded-xl p-3 bg-gray-50 space-y-2">
              <input
                autoFocus
                type="text"
                placeholder="Search by name or EID…"
                value={resourceSearch}
                onChange={(e) => setResourceSearch(e.target.value)}
                className="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-400"
              />
              {filteredResources.slice(0, 8).map((r) => (
                  <button
                    key={r.id}
                    onClick={() => handleAddMember(r.id)}
                    disabled={createAssignmentMutation.isPending}
                    className="w-full text-left px-3 py-2 rounded-lg hover:bg-white text-sm text-gray-800 border border-transparent hover:border-gray-200 transition-colors"
                  >
                  <span className="font-medium">{r.displayName}</span>
                  <span className="text-gray-400 ml-2 text-xs">{r.eid}</span>
                  {r.chapter && <span className="text-gray-300 ml-1 text-xs">· {r.chapter}</span>}
                </button>
              ))}
              <button
                onClick={() => { setAddingMember(false); setResourceSearch(""); }}
                className="text-xs text-gray-400 hover:text-gray-600"
              >
                Cancel
              </button>
            </div>
          )}

          <div className="space-y-3">
            {effectiveAssignments.map((alloc) => {
              const edit = getEdit(alloc.id);
              const isDirty = Object.keys(edit).length > 0;
              const meta = alloc.metadata as { includeSaturday?: boolean } | null;
              const inclSat = edit.includeSaturday ?? meta?.includeSaturday ?? false;

              return (
                <div
                  key={alloc.id}
                  className="border border-gray-100 rounded-xl p-3 space-y-2 bg-white"
                >
                  {/* Resource name + delete */}
                  <div className="flex items-center justify-between">
                    <div>
                      <span className="text-sm font-medium text-gray-800">{alloc.resource?.displayName}</span>
                      <span className="text-xs text-gray-400 ml-1.5">{alloc.resource?.eid}</span>
                    </div>
                    {confirmDelete === alloc.id ? (
                      <div className="flex items-center gap-2">
                        <span className="text-xs text-red-600">Remove?</span>
                        <button
                          onClick={() => { deleteMutation.mutate({ id: getPlanningEntryMutationId(alloc) }); setConfirmDelete(null); }}
                          className="text-xs text-red-600 font-medium hover:text-red-800"
                        >
                          Yes
                        </button>
                        <button
                          onClick={() => setConfirmDelete(null)}
                          className="text-xs text-gray-400 hover:text-gray-600"
                        >
                          No
                        </button>
                      </div>
                    ) : (
                      <button
                        onClick={() => setConfirmDelete(alloc.id)}
                        className="text-xs text-red-400 hover:text-red-600"
                      >
                        Remove
                      </button>
                    )}
                  </div>

                  {/* Role */}
                  <input
                    type="text"
                    value={edit.role ?? alloc.role ?? ""}
                    onChange={(e) => setEdit(alloc.id, { role: e.target.value })}
                    className="w-full border border-gray-200 rounded-lg px-3 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-brand-400"
                    placeholder="Role"
                  />

                  {/* Dates + hours */}
                  <div className="grid grid-cols-3 gap-2">
                    <div>
                      <label className="block text-[10px] text-gray-400 mb-0.5">Start</label>
                      <DateInput
                        value={edit.startDate ?? toDateInput(alloc.startDate)}
                        onChange={(v) => setEdit(alloc.id, { startDate: v })}
                        className="w-full border border-gray-200 rounded-lg px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-brand-400"
                      />
                    </div>
                    <div>
                      <label className="block text-[10px] text-gray-400 mb-0.5">End</label>
                      <DateInput
                        value={edit.endDate ?? toDateInput(alloc.endDate)}
                        onChange={(v) => setEdit(alloc.id, { endDate: v })}
                        className="w-full border border-gray-200 rounded-lg px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-brand-400"
                      />
                    </div>
                    <div>
                      <label className="block text-[10px] text-gray-400 mb-0.5">h/day</label>
                      <input
                        type="number"
                        min={0.5}
                        max={24}
                        step={0.5}
                        value={edit.hoursPerDay ?? alloc.hoursPerDay}
                        onChange={(e) => setEdit(alloc.id, { hoursPerDay: parseFloat(e.target.value) })}
                        className="w-full border border-gray-200 rounded-lg px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-brand-400"
                      />
                    </div>
                  </div>

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

                  {/* Save button */}
                  {isDirty && (
                    <button
                      onClick={() => saveEdit(alloc.id)}
                      disabled={updateMutation.isPending}
                      className="w-full py-1.5 rounded-lg text-xs font-medium bg-brand-600 text-white hover:bg-brand-700 disabled:opacity-50"
                    >
                      {updateMutation.isPending ? "Saving…" : "Save changes"}
                    </button>
                  )}
                </div>
              );
            })}

            {effectiveAssignments.length === 0 && (
              <div className="text-center py-8 text-sm text-gray-400">
                No team members yet. Add one above.
              </div>
            )}
          </div>
        </section>
      </div>
    </PanelShell>
  );
}

function PanelShell({ children, onClose }: { children: React.ReactNode; onClose: () => void }) {
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        e.preventDefault();
        onClose();
      }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [onClose]);

  return (
    <div className="fixed inset-y-0 right-0 w-[420px] bg-white border-l border-gray-200 shadow-2xl z-40 flex flex-col">
      <div className="flex items-center justify-between px-5 py-3 border-b border-gray-100">
        <span className="text-sm font-semibold text-gray-700">Project Details</span>
        <button
          onClick={onClose}
          className="w-7 h-7 rounded-lg flex items-center justify-center text-gray-400 hover:text-gray-700 hover:bg-gray-100 transition-colors text-lg leading-none"
        >
          &times;
        </button>
      </div>
      <div className="flex flex-col flex-1 min-h-0">{children}</div>
    </div>
  );
}
