"use client";

import * as React from "react";
import { CalendarDays, ChevronDown, RefreshCw, Users } from "lucide-react";
import { toast } from "sonner";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { ReactSelect } from "@/components/ui/react-select";
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { Loading } from "@/components/ui/loading";
import { SalesTargetProgressBar } from "@/components/sales-targets/sales-target-progress-bar";
import {
  useGetTeamRepTargetsQuery,
  useUpsertRepSalesTargetMutation,
  type RepTargetMember,
} from "@/api/rtk/sales-targets-api";
import type { Team } from "@/api/rtk/teams-api";
import { formatTeamLabelForUi } from "@/lib/deal-display";
import { formatSupportSlot } from "@/components/sales-targets/sales-targets-helpers";
import { cn } from "@/lib/utils";

type MemberRow = {
  rowKey: string;
  userId: string;
  name: string;
  target: string;
  /** Saved amount from API; null = not yet assigned */
  savedAmount: number | null;
  isTeamLead: boolean;
  isSupport?: boolean;
  supportRole?: string | null;
  parentAeUserId?: string;
};

function memberRowKey(row: {
  userId: string;
  isSupport?: boolean;
  parentAeUserId?: string;
  assignmentId?: string;
}) {
  if (row.isSupport) {
    return `support:${row.parentAeUserId ?? "unknown"}:${row.assignmentId ?? row.userId}`;
  }
  return row.userId;
}

type Props = {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  year: number;
  month: number;
  teams: Team[];
  initialTeamId?: string;
  lockTeam?: boolean;
  memberTargetSeed?: Record<string, number | null | undefined>;
  highlightUserId?: string;
  onSaved?: () => void;
};

function parseNum(s: string) {
  const n = Number.parseFloat(s.replace(/,/g, ""));
  return Number.isFinite(n) && n >= 0 ? n : undefined;
}

function formatMoney(n: number | null | undefined) {
  if (n == null || Number.isNaN(n)) return "—";
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    maximumFractionDigits: 0,
  }).format(n);
}

function sumRepTargets(rows: MemberRow[]) {
  return rows
    .filter((row) => !row.isSupport)
    .reduce((sum, row) => sum + (parseNum(row.target) ?? 0), 0);
}

function extractErrorMessage(err: unknown): string | undefined {
  if (
    err &&
    typeof err === "object" &&
    "data" in err &&
    err.data &&
    typeof err.data === "object" &&
    "message" in err.data
  ) {
    const msg = (err.data as { message?: unknown }).message;
    if (typeof msg === "string") return msg;
    if (Array.isArray(msg)) return msg.map(String).join(", ");
  }
  return undefined;
}

function rowHasPendingSave(row: MemberRow): boolean {
  const amount = parseNum(row.target);
  if (amount == null) return false;
  if (row.savedAmount == null) return true;
  return amount > row.savedAmount;
}

function mapApiMemberToRow(
  m: RepTargetMember,
  isTeamLead: boolean,
  useSeed: boolean,
  memberTargetSeed?: Record<string, number | null | undefined>,
): MemberRow[] {
  const savedAmount = m.aeTarget;
  const amount =
    savedAmount ??
    ((useSeed ? memberTargetSeed?.[m.userId] : undefined) ?? null);
  const aeRow: MemberRow = {
    rowKey: memberRowKey({ userId: m.userId }),
    userId: m.userId,
    name: m.name,
    target: amount != null ? String(amount) : "",
    savedAmount,
    isTeamLead,
  };
  const supportRows = (m.supportMembers ?? []).map((support) => {
    const supportSaved = support.aeTarget;
    const supportAmount =
      supportSaved ??
      ((useSeed ? memberTargetSeed?.[support.userId] : undefined) ?? null);
    const row = {
      userId: support.userId,
      name: support.name,
      target: supportAmount != null ? String(supportAmount) : "",
      savedAmount: supportSaved,
      isTeamLead: false,
      isSupport: true as const,
      supportRole: support.supportRole,
      parentAeUserId: m.userId,
      assignmentId: support.assignmentId,
    };
    return {
      ...row,
      rowKey: memberRowKey(row),
    };
  });
  return [aeRow, ...supportRows];
}

function MemberTargetRows({
  rows,
  missingTeamTarget = false,
  highlightRowKey,
  onTargetChange,
}: {
  rows: MemberRow[];
  missingTeamTarget?: boolean;
  highlightRowKey?: string;
  onTargetChange: (rowKey: string, target: string) => void;
}) {
  return (
    <ul className="divide-y divide-border/50 overflow-hidden rounded-xl border border-border/60 bg-card/50">
      {rows.map((row) => {
        const current = parseNum(row.target);
        const belowSaved =
          row.savedAmount != null &&
          current != null &&
          current < row.savedAmount;

        return (
          <li
            key={row.rowKey}
            id={`rep-target-row-${row.rowKey}`}
            className={cn(
              "flex flex-col gap-2 px-4 py-3 sm:flex-row sm:items-center sm:justify-between",
              row.savedAmount != null && "bg-muted/25",
              belowSaved && "bg-destructive/5",
              row.isSupport && "bg-muted/15 pl-8",
              highlightRowKey === row.rowKey &&
                "ring-2 ring-accent/50 ring-offset-2 ring-offset-background",
            )}
          >
            <div className="min-w-0 flex-1">
              <div className="flex flex-wrap items-center gap-2">
                <p
                  className={cn(
                    "truncate font-semibold text-text",
                    row.isSupport && "text-sm text-muted-foreground",
                  )}
                >
                  {row.name}
                </p>
                {row.isTeamLead && (
                  <Badge
                    variant="outline"
                    className="border-accent/40 bg-accent/10 px-1.5 py-0 font-['Lexend_Deca'] text-[10px] font-semibold text-accent"
                  >
                    Team lead
                  </Badge>
                )}
                {row.isSupport && (
                  <Badge
                    variant="outline"
                    className="border-border/60 bg-background/60 px-1.5 py-0 font-['Lexend_Deca'] text-[10px] font-semibold text-muted-foreground"
                  >
                    {formatSupportSlot(row.supportRole)}
                  </Badge>
                )}
              </div>
              {row.savedAmount != null && (
                <p className="mt-1 text-[10px] text-muted-foreground">
                  Assigned · can only increase (min{" "}
                  {formatMoney(row.savedAmount)})
                </p>
              )}
              {belowSaved && (
                <p className="mt-0.5 text-[10px] text-destructive">
                  Amount cannot be below the saved target
                </p>
              )}
            </div>
            <div className="flex shrink-0 items-center justify-end sm:w-[180px]">
              <div className="relative w-full max-w-[180px]">
                <span
                  className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground"
                  aria-hidden
                >
                  $
                </span>
                <Input
                  type="number"
                  min={row.savedAmount ?? 0}
                  step={100}
                  inputMode="numeric"
                  className="h-9 w-full pl-7 text-right tabular-nums"
                  value={row.target}
                  placeholder="0"
                  disabled={false}
                  aria-label={`Target for ${row.name}`}
                  onChange={(e) => onTargetChange(row.rowKey, e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") e.preventDefault();
                  }}
                />
              </div>
            </div>
          </li>
        );
      })}
    </ul>
  );
}

function SummaryStat({ label, value }: { label: string; value: string }) {
  return (
    <div className="min-w-0 rounded-lg border border-border/50 bg-card/80 px-3 py-2.5">
      <p className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">
        {label}
      </p>
      <p className="mt-0.5 truncate text-sm font-extrabold tabular-nums text-text font-['Lexend'] sm:text-base">
        {value}
      </p>
    </div>
  );
}

export function AssignRepTargetsModal({
  open,
  onOpenChange,
  year,
  month,
  teams,
  initialTeamId,
  lockTeam = false,
  memberTargetSeed,
  highlightUserId,
  onSaved,
}: Props) {
  const [teamId, setTeamId] = React.useState(initialTeamId ?? teams[0]?.id ?? "");
  const [rows, setRows] = React.useState<MemberRow[]>([]);
  const [teamLeadsOpen, setTeamLeadsOpen] = React.useState(true);
  const [upsertRep, { isLoading: saving }] = useUpsertRepSalesTargetMutation();

  React.useEffect(() => {
    if (!open) return;
    setTeamId(initialTeamId ?? teams[0]?.id ?? "");
  }, [open, initialTeamId, teams]);

  const effectiveTeamId = lockTeam ? (initialTeamId ?? teamId) : teamId;

  const { data, isLoading, isError, isFetching, refetch } = useGetTeamRepTargetsQuery(
    { teamId: effectiveTeamId, year, month },
    { skip: !open || !effectiveTeamId, refetchOnMountOrArgChange: true },
  );

  React.useEffect(() => {
    if (!open) return;
    setRows([]);
    setTeamLeadsOpen(true);
  }, [open, effectiveTeamId, year, month]);

  React.useEffect(() => {
    if (!data) return;
    const useSeed = lockTeam && memberTargetSeed;
    const teamLeads = data.teamLeads ?? [];
    const members = data.members ?? [];
    setRows([
      ...teamLeads.flatMap((m) =>
        mapApiMemberToRow(m, true, Boolean(useSeed), memberTargetSeed),
      ),
      ...members.flatMap((m) =>
        mapApiMemberToRow(m, false, Boolean(useSeed), memberTargetSeed),
      ),
    ]);
  }, [data, memberTargetSeed, lockTeam]);

  const teamLeadRows = React.useMemo(
    () => rows.filter((r) => r.isTeamLead && !r.isSupport),
    [rows],
  );
  const repRows = React.useMemo(
    () => rows.filter((r) => !r.isTeamLead && !r.isSupport),
    [rows],
  );

  const highlightRowKey = React.useMemo(() => {
    if (!highlightUserId) return undefined;
    const highlighted =
      rows.find((r) => r.userId === highlightUserId && r.isSupport) ??
      rows.find((r) => r.userId === highlightUserId);
    return highlighted?.rowKey;
  }, [highlightUserId, rows]);

  const rowsForAe = React.useCallback(
    (aeUserId: string) => {
      const ae = rows.find((r) => r.userId === aeUserId);
      if (!ae) return [];
      const supports = rows.filter(
        (r) => r.isSupport && r.parentAeUserId === aeUserId,
      );
      return [ae, ...supports];
    },
    [rows],
  );

  React.useEffect(() => {
    if (!open || !highlightRowKey || rows.length === 0) return;
    const highlighted = rows.find((r) => r.rowKey === highlightRowKey);
    const aeUserId =
      highlighted?.isSupport && highlighted.parentAeUserId
        ? highlighted.parentAeUserId
        : highlighted?.userId;
    if (aeUserId && teamLeadRows.some((r) => r.userId === aeUserId)) {
      setTeamLeadsOpen(true);
    }
    const frame = requestAnimationFrame(() => {
      document
        .getElementById(`rep-target-row-${highlightRowKey}`)
        ?.scrollIntoView({ block: "nearest", behavior: "smooth" });
    });
    return () => cancelAnimationFrame(frame);
  }, [open, highlightRowKey, rows, teamLeadRows]);

  const updateRowTarget = React.useCallback((rowKey: string, target: string) => {
    setRows((prev) =>
      prev.map((r) => (r.rowKey === rowKey ? { ...r, target } : r)),
    );
  }, []);

  const rowsToSave = React.useMemo(
    () => rows.filter(rowHasPendingSave),
    [rows],
  );
  const assignedCount = React.useMemo(
    () => rows.filter((r) => r.savedAmount != null).length,
    [rows],
  );
  const hasDecreaseAttempt = React.useMemo(
    () =>
      rows.some((r) => {
        const n = parseNum(r.target);
        return r.savedAmount != null && n != null && n < r.savedAmount;
      }),
    [rows],
  );

  // teamAeTarget is now the *computed live sum* of rep targets for the team (not a separate cap).
  const teamAeTarget = data?.teamAeTarget ?? null;
  const assignedTotal = React.useMemo(() => sumRepTargets(rows), [rows]);

  // We no longer block on "team cap". These are informational only.
  const remaining =
    teamAeTarget != null && teamAeTarget > 0
      ? teamAeTarget - assignedTotal
      : null;
  const allocationPct =
    teamAeTarget != null && teamAeTarget > 0
      ? Math.min(100, (assignedTotal / teamAeTarget) * 100)
      : null;

  // No more hard cap enforcement — team total IS the sum of reps.
  const overTeamCap = false;
  const missingTeamTarget = false;

  const periodLabel = data?.label ?? "This month";
  const teamName =
    data?.teamName ?? teams.find((t) => t.id === effectiveTeamId)?.name;

  const handleSaveAll = async () => {
    if (!effectiveTeamId) {
      toast.error("Select a team");
      return;
    }
    // No longer requires a pre-set team cap. Team total is the sum of the individual targets you assign.
    if (hasDecreaseAttempt) {
      toast.error("Rep targets can only be increased, not decreased");
      return;
    }
    const toSave = rowsToSave;
    if (toSave.length === 0) {
      toast.error(
        "Enter a higher target for at least one member, or assign a new member",
      );
      return;
    }
    try {
      for (const row of toSave) {
        await upsertRep({
          teamId: effectiveTeamId,
          userId: row.userId,
          year,
          month,
          roleKind: "AE",
          targetAmount: parseNum(row.target),
        }).unwrap();
      }
      toast.success("Member targets saved");
      onOpenChange(false);
      onSaved?.();
    } catch (err) {
      toast.error(extractErrorMessage(err) ?? "Failed to save targets");
    }
  };

  const saveDisabled =
    saving ||
    !rows.length ||
    !effectiveTeamId ||
    hasDecreaseAttempt ||
    rowsToSave.length === 0;

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent
        className="flex max-h-[min(90vh,720px)] flex-col gap-0 overflow-hidden p-0 sm:max-w-lg"
        aria-describedby={undefined}
      >
        <DialogHeader className="space-y-3 border-b border-border/50 px-6 pt-6 pb-4">
          <div className="flex items-start justify-between gap-3 pr-8">
            <div className="min-w-0 space-y-1">
              <DialogTitle className="text-left font-['Lexend'] text-lg font-extrabold">
                Assign rep targets
              </DialogTitle>
              <div className="flex flex-wrap items-center gap-2 text-sm text-muted-foreground">
                {teamName && (
                  <span className="inline-flex items-center gap-1 font-medium text-foreground">
                    <Users className="h-3.5 w-3.5 shrink-0 text-accent" aria-hidden />
                    {teamName}
                  </span>
                )}
                <Badge variant="outline" className="gap-1 font-normal">
                  <CalendarDays className="h-3 w-3" aria-hidden />
                  {periodLabel}
                </Badge>
                {!isLoading && rows.length > 0 && (
                  <Badge variant="secondary" className="font-normal">
                    {assignedCount > 0
                      ? `${assignedCount} of ${rows.length} assigned`
                      : `${rows.length} members`}
                  </Badge>
                )}
              </div>
            </div>
            <Button
              type="button"
              variant="ghost"
              size="icon-sm"
              className="shrink-0"
              disabled={!effectiveTeamId || isLoading}
              onClick={() => void refetch()}
              aria-label="Reload members"
            >
              <RefreshCw
                className={cn("h-4 w-4", isFetching && "animate-spin")}
                aria-hidden
              />
            </Button>
          </div>
        </DialogHeader>

        <div className="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-6 py-4">
          {effectiveTeamId && !isLoading && (
            <div className="space-y-3 rounded-xl border border-border/60 bg-muted/20 p-3">
              <div className="grid gap-2 sm:grid-cols-3">
                <SummaryStat
                  label="Team total (sum of reps)"
                  value={formatMoney(teamAeTarget)}
                />
                <SummaryStat
                  label="Assigned (this session)"
                  value={formatMoney(assignedTotal)}
                />
                <SummaryStat
                  label="Difference"
                  value={formatMoney(
                    remaining != null ? Math.max(0, remaining) : null,
                  )}
                />
              </div>
              {allocationPct != null && (
                <div className="space-y-1">
                  <p className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">
                    Allocation vs current team total
                  </p>
                  <SalesTargetProgressBar pct={allocationPct} />
                </div>
              )}
              <p className="text-[11px] text-muted-foreground">
                Team total is the sum of AE rep targets only. Support slot targets
                are tracked separately and do not add to the team total.
              </p>
            </div>
          )}

          {!lockTeam && (
            <div className="grid gap-2">
              <Label htmlFor="assign-rep-team">Team</Label>
              <ReactSelect
                id="assign-rep-team"
                value={teamId}
                onValueChange={setTeamId}
                options={teams.map((t) => ({
                  value: t.id,
                  label: formatTeamLabelForUi(t.name),
                }))}
                placeholder="Select team"
                aria-label="Team"
              />
            </div>
          )}

          <div className="min-h-0 flex-1">
            {isLoading ? (
              <div className="py-8">
                <Loading variant="api" layout="section" message="Loading members…" />
              </div>
            ) : isError ? (
              <p className="rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive">
                Could not load team members. Check permissions or initialize this
                target month.
              </p>
            ) : rows.length === 0 ? (
              <p className="rounded-lg border border-dashed border-border/60 p-6 text-center text-sm text-muted-foreground">
                No eligible members on this team.
              </p>
            ) : (
              <div className="flex flex-col gap-4">
                {teamLeadRows.length > 0 && (
                  <Collapsible
                    open={teamLeadsOpen}
                    onOpenChange={setTeamLeadsOpen}
                    className="group/team-leads"
                  >
                    <CollapsibleTrigger
                      type="button"
                      className="flex w-full items-center justify-between gap-2 rounded-lg border border-border/60 bg-muted/20 px-3 py-2.5 text-left font-['Lexend_Deca'] text-xs font-bold uppercase tracking-wider text-muted-foreground hover:bg-muted/30"
                    >
                      <span>Team leads</span>
                      <span className="flex items-center gap-2">
                        <Badge variant="secondary" className="font-normal normal-case">
                          {teamLeadRows.length}
                        </Badge>
                        <ChevronDown
                          className="size-4 shrink-0 transition-transform group-data-[state=open]/team-leads:rotate-180"
                          aria-hidden
                        />
                      </span>
                    </CollapsibleTrigger>
                    <CollapsibleContent className="pt-3">
                      <div className="flex flex-col gap-4">
                        {teamLeadRows.map((lead) => (
                          <MemberTargetRows
                            key={lead.userId}
                            rows={rowsForAe(lead.userId)}
                            missingTeamTarget={false}
                            highlightRowKey={highlightRowKey}
                            onTargetChange={updateRowTarget}
                          />
                        ))}
                      </div>
                    </CollapsibleContent>
                  </Collapsible>
                )}
                {repRows.length > 0 && (
                  <div className="space-y-3">
                    {teamLeadRows.length > 0 && (
                      <p className="font-['Lexend_Deca'] text-xs font-bold uppercase tracking-wider text-muted-foreground">
                        Reps
                      </p>
                    )}
                    <div className="flex flex-col gap-4">
                      {repRows.map((rep) => (
                        <MemberTargetRows
                          key={rep.userId}
                          rows={rowsForAe(rep.userId)}
                          missingTeamTarget={false}
                          highlightRowKey={highlightRowKey}
                          onTargetChange={updateRowTarget}
                        />
                      ))}
                    </div>
                  </div>
                )}
              </div>
            )}
          </div>
        </div>

        <DialogFooter className="gap-2 border-t border-border/50 bg-muted/10 px-6 py-4 sm:justify-between">
          <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            type="button"
            disabled={saveDisabled}
            onClick={() => void handleSaveAll()}
          >
            {saving
              ? "Saving…"
              : rowsToSave.length > 0
                ? `Save${rowsToSave.length > 1 ? ` (${rowsToSave.length})` : ""}`
                : "No changes"}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
