'use client';

import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { AlertTriangle, Check, ChevronDown, ChevronLeft, Loader2 } from 'lucide-react';
import { toast } from 'sonner';

import { Button } from '@/components/ui/button';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Textarea } from '@/components/ui/textarea';
import { YachtPicker } from '@/components/yachts/yacht-picker';
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import { cn } from '@/lib/utils';
import {
  PIPELINE_STAGES,
  STAGE_BADGE,
  STAGE_DOT,
  STAGE_LABELS,
  safeStage,
  type PipelineStage,
} from '@/components/clients/pipeline-constants';
import { canTransitionStage } from '@/lib/constants';
import { usePermissions } from '@/hooks/use-permissions';

interface InlineStagePickerProps {
  interestId: string;
  currentStage: string;
  /** Whether to render the chevron after the stage label. Default true. */
  showChevron?: boolean;
  /** Stop the parent's click propagation when used inside a clickable card. */
  stopPropagation?: boolean;
  className?: string;
  /** Current yacht linked to the interest (null if missing). When the rep
   *  tries to leave Enquiry without a yacht, the picker switches into an
   *  inline prereq view that lets them link a yacht and proceed in one
   *  flow instead of bouncing them out to the form. */
  currentYachtId?: string | null;
  /** Client owning the interest - scopes the inline yacht-picker so the
   *  rep only sees yachts that actually belong to this lead. */
  clientId?: string;
}

/**
 * Click-to-change stage chip. Replaces the modal-based InterestStagePicker
 * for inline editing - user clicks the chip, picks a new stage from the
 * popover (with optional reason), commits in one click. The popover stays
 * compact: a small reason field above the stage list, and clicking any stage
 * fires the mutation immediately.
 */
export function InlineStagePicker({
  interestId,
  currentStage,
  showChevron = true,
  stopPropagation = false,
  className,
  currentYachtId,
  clientId,
}: InlineStagePickerProps) {
  const queryClient = useQueryClient();
  const [open, setOpen] = useState(false);
  const [pendingStage, setPendingStage] = useState<string | null>(null);
  // F23: when the rep picks a non-Enquiry stage without a yacht linked,
  // we drop into a yacht-picker view in the popover. yachtPrereqTarget
  // holds the stage they were trying to reach; submitting the picker
  // PATCHes the yachtId, then chains the original stage move.
  const [yachtPrereqTarget, setYachtPrereqTarget] = useState<PipelineStage | null>(null);
  const [yachtPrereqId, setYachtPrereqId] = useState<string | null>(null);
  const [linkingYacht, setLinkingYacht] = useState(false);
  // When a user picks a stage that isn't a legal next step (and has the
  // override permission), the popover transitions into a confirm view
  // that asks for a reason before committing. Reasons are not exposed
  // for legal transitions - they're stored as audit-log notes on the
  // interest's history, accessible via the activity timeline.
  const [overrideTarget, setOverrideTarget] = useState<PipelineStage | null>(null);
  const [overrideReason, setOverrideReason] = useState('');
  // When dropping the stage back to enquiry on an interest with linked
  // berths, prompt the rep whether to keep or unlink them. Going back to
  // enquiry usually means restarting the lead, so the berth association
  // is often stale; offering a one-tap unlink prevents the public-map +
  // recommender from showing the berths as "under offer" for a dead deal.
  const [openConfirmTarget, setOpenConfirmTarget] = useState<PipelineStage | null>(null);
  const [unlinking, setUnlinking] = useState(false);
  const { can } = usePermissions();
  const canOverride = can('interests', 'override_stage');

  const stage = safeStage(currentStage);

  // Fetch the linked-berth list lazily so we know whether to surface the
  // unlink-prompt when the rep drops the stage back to enquiry.
  const { data: linkedBerths } = useQuery<{ data: Array<{ berthId: string }> }>({
    queryKey: ['interest-berths', interestId, 'count-only'],
    queryFn: () => apiFetch(`/api/v1/interests/${interestId}/berths`),
    enabled: open,
    staleTime: 30_000,
  });
  const linkedBerthCount = linkedBerths?.data.length ?? 0;

  const mutation = useMutation({
    mutationFn: async ({ next, reason }: { next: PipelineStage; reason: string | null }) => {
      const needsOverride = !canTransitionStage(stage, next);
      const useOverride = needsOverride && canOverride;
      return apiFetch(`/api/v1/interests/${interestId}/stage`, {
        method: 'PATCH',
        body: {
          pipelineStage: next,
          reason: reason ?? (useOverride ? 'Manual override (inline)' : undefined),
          override: useOverride || undefined,
        },
      });
    },
    onSuccess: (_data, vars) => {
      queryClient.invalidateQueries({ queryKey: ['interests', interestId] });
      queryClient.invalidateQueries({ queryKey: ['interests'] });
      setOpen(false);
      setOverrideTarget(null);
      setOverrideReason('');
      setPendingStage(null);
      toast.success(`Stage moved to ${STAGE_LABELS[vars.next]}`);
    },
    onError: (err) => {
      setPendingStage(null);
      toastError(err);
    },
  });

  function pick(next: PipelineStage) {
    if (next === stage) {
      setOpen(false);
      return;
    }
    // Rewind-to-enquiry guard: if the rep is dropping the stage back to
    // 'enquiry' AND the interest still has linked berths, intercept to ask
    // whether to unlink them. Skipped when there are no linked berths
    // (the prompt would be noise) or when the rep is already at enquiry.
    if (next === 'enquiry' && stage !== 'enquiry' && linkedBerthCount > 0) {
      setOpenConfirmTarget(next);
      setOpen(false);
      return;
    }
    // F23: leaving Enquiry without a yacht linked is the one hard prereq
    // the service enforces. Instead of bouncing them to the validation
    // error toast, drop into an inline yacht-picker so the rep can fix
    // it in place. Same flow chains the stage move after the link.
    if (stage === 'enquiry' && next !== 'enquiry' && !currentYachtId) {
      setYachtPrereqTarget(next);
      setYachtPrereqId(null);
      return;
    }
    const isOverride = !canTransitionStage(stage, next);
    if (isOverride && canOverride) {
      // Switch into the confirm view rather than firing the mutation
      // immediately - overrides bypass the transition guard so a reason
      // is genuinely useful for the audit trail.
      setOverrideTarget(next);
      setOverrideReason('');
      return;
    }
    setPendingStage(next);
    mutation.mutate({ next, reason: null });
  }

  async function commitYachtPrereq() {
    if (!yachtPrereqTarget || !yachtPrereqId) return;
    setLinkingYacht(true);
    try {
      await apiFetch(`/api/v1/interests/${interestId}`, {
        method: 'PATCH',
        body: { yachtId: yachtPrereqId },
      });
      queryClient.invalidateQueries({ queryKey: ['interests', interestId] });
      queryClient.invalidateQueries({ queryKey: ['interests'] });
      const target = yachtPrereqTarget;
      setYachtPrereqTarget(null);
      setYachtPrereqId(null);
      setPendingStage(target);
      mutation.mutate({ next: target, reason: null });
    } catch (err) {
      toastError(err);
    } finally {
      setLinkingYacht(false);
    }
  }

  function cancelYachtPrereq() {
    setYachtPrereqTarget(null);
    setYachtPrereqId(null);
  }

  async function unlinkAllAndOpen(target: PipelineStage) {
    setUnlinking(true);
    try {
      const ids = (linkedBerths?.data ?? []).map((b) => b.berthId);
      await Promise.all(
        ids.map((berthId) =>
          apiFetch(`/api/v1/interests/${interestId}/berths/${berthId}`, { method: 'DELETE' }),
        ),
      );
      // After unlinking, the canTransition table might no longer flag this
      // as an override - re-evaluate just in case.
      const isOverride = !canTransitionStage(stage, target);
      mutation.mutate({
        next: target,
        reason: isOverride ? 'Reverted to New Enquiry and unlinked all berths' : null,
      });
      setOpenConfirmTarget(null);
    } catch (err) {
      toastError(err);
    } finally {
      setUnlinking(false);
    }
  }

  function keepBerthsAndOpen(target: PipelineStage) {
    const isOverride = !canTransitionStage(stage, target);
    setPendingStage(target);
    mutation.mutate({
      next: target,
      reason: isOverride ? 'Reverted to New Enquiry (kept linked berths)' : null,
    });
    setOpenConfirmTarget(null);
  }

  function commitOverride() {
    if (!overrideTarget) return;
    setPendingStage(overrideTarget);
    mutation.mutate({
      next: overrideTarget,
      reason: overrideReason.trim() || 'Manual override (inline)',
    });
  }

  function cancelOverride() {
    setOverrideTarget(null);
    setOverrideReason('');
  }

  return (
    <>
      <Popover
        open={open}
        onOpenChange={(o) => {
          if (mutation.isPending || linkingYacht) return;
          setOpen(o);
          if (!o) {
            cancelOverride();
            cancelYachtPrereq();
          }
        }}
      >
        <PopoverTrigger asChild>
          <button
            type="button"
            onClick={(e) => {
              if (stopPropagation) e.stopPropagation();
            }}
            className={cn(
              'inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-sm font-medium',
              'transition-colors hover:brightness-95 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring',
              STAGE_BADGE[stage],
              className,
            )}
            aria-label={`Pipeline stage: ${STAGE_LABELS[stage]}. Click to change.`}
          >
            <span>{STAGE_LABELS[stage]}</span>
            {mutation.isPending ? (
              <Loader2 className="size-3 animate-spin" aria-hidden />
            ) : showChevron ? (
              <ChevronDown className="size-3 opacity-70" aria-hidden />
            ) : null}
          </button>
        </PopoverTrigger>
        <PopoverContent
          align="start"
          className="w-72 p-0"
          onClick={(e) => stopPropagation && e.stopPropagation()}
        >
          {yachtPrereqTarget ? (
            // F23: inline yacht-prereq view - only reached when the rep
            // picked a non-Enquiry stage without a yacht linked. Surfaces
            // a yacht-picker right inside the popover so they can fix
            // the prereq and move the stage in one flow.
            <div className="p-3 space-y-3">
              <div className="flex items-start gap-2">
                <AlertTriangle className="size-4 shrink-0 text-amber-600 mt-0.5" aria-hidden />
                <div className="text-sm">
                  <p className="font-medium text-foreground">Yacht required</p>
                  <p className="text-xs text-muted-foreground">
                    A yacht must be linked before leaving Enquiry. Pick one below to move to{' '}
                    {STAGE_LABELS[yachtPrereqTarget]}.
                  </p>
                </div>
              </div>
              <div className="space-y-1">
                <label className="text-xs font-medium text-muted-foreground">Linked yacht</label>
                <YachtPicker
                  value={yachtPrereqId}
                  onChange={(id) => setYachtPrereqId(id)}
                  ownerFilter={clientId ? { type: 'client', id: clientId } : undefined}
                  disabled={linkingYacht || mutation.isPending}
                />
              </div>
              <div className="flex items-center justify-between gap-2">
                <Button
                  type="button"
                  variant="ghost"
                  size="sm"
                  onClick={cancelYachtPrereq}
                  disabled={linkingYacht || mutation.isPending}
                  className="gap-1"
                >
                  <ChevronLeft className="size-3.5" aria-hidden />
                  Back
                </Button>
                <Button
                  type="button"
                  size="sm"
                  onClick={commitYachtPrereq}
                  disabled={!yachtPrereqId || linkingYacht || mutation.isPending}
                >
                  {(linkingYacht || mutation.isPending) && (
                    <Loader2 className="size-3.5 animate-spin mr-1" aria-hidden />
                  )}
                  Link yacht & advance
                </Button>
              </div>
            </div>
          ) : overrideTarget ? (
            // Confirm-override view: only reached when the user picked a
            // stage that isn't a legal next step. Reason is optional but
            // strongly nudged for the audit log.
            <div className="p-3 space-y-3">
              <div className="flex items-start gap-2">
                <AlertTriangle className="size-4 shrink-0 text-amber-600 mt-0.5" aria-hidden />
                <div className="text-sm">
                  <p className="font-medium text-foreground">Override transition</p>
                  <p className="text-xs text-muted-foreground">
                    {STAGE_LABELS[stage]} → {STAGE_LABELS[overrideTarget]} isn&apos;t a standard
                    next step. The change will be flagged in the audit log.
                  </p>
                </div>
              </div>
              <div>
                <label
                  htmlFor="stage-override-reason"
                  className="text-xs font-medium text-muted-foreground"
                >
                  Reason (optional but recommended)
                </label>
                <Textarea
                  id="stage-override-reason"
                  value={overrideReason}
                  onChange={(e) => setOverrideReason(e.target.value)}
                  placeholder="e.g. Skipping EOI, client signed contract directly"
                  rows={2}
                  className="mt-1 text-sm"
                  disabled={mutation.isPending}
                  autoFocus
                />
              </div>
              <div className="flex items-center justify-between gap-2">
                <Button
                  type="button"
                  variant="ghost"
                  size="sm"
                  onClick={cancelOverride}
                  disabled={mutation.isPending}
                  className="gap-1"
                >
                  <ChevronLeft className="size-3.5" aria-hidden />
                  Back
                </Button>
                <Button
                  type="button"
                  size="sm"
                  onClick={commitOverride}
                  disabled={mutation.isPending}
                >
                  {mutation.isPending && (
                    <Loader2 className="size-3.5 animate-spin mr-1" aria-hidden />
                  )}
                  Confirm override
                </Button>
              </div>
            </div>
          ) : (
            // Default view: just the stage list. No upfront textarea -
            // earlier UX put a "Reason (optional)…" field at the top
            // which read as visually noisy for the >90% of changes that
            // are normal transitions and never get a reason attached.
            <ul role="listbox" aria-label="Pipeline stages" className="py-1">
              {PIPELINE_STAGES.map((s) => {
                const isCurrent = s === stage;
                const isPending = pendingStage === s && mutation.isPending;
                const isOverride = s !== stage && !canTransitionStage(stage, s);
                const blockedByPermission = isOverride && !canOverride;
                return (
                  <li key={s}>
                    <button
                      type="button"
                      role="option"
                      aria-selected={isCurrent}
                      disabled={mutation.isPending || blockedByPermission}
                      onClick={() => pick(s)}
                      title={
                        blockedByPermission
                          ? `Override required (you don't have permission)`
                          : isOverride
                            ? 'Non-standard transition - confirm step required'
                            : undefined
                      }
                      className={cn(
                        'flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm',
                        'transition-colors hover:bg-muted/60 disabled:opacity-50 disabled:cursor-not-allowed',
                        isCurrent && 'font-medium',
                      )}
                    >
                      {/* Colored chip (mirrors the inline stage badge) - turns
                         the picker into a visual scan rather than just a list. */}
                      <span
                        className={cn('inline-flex h-5 w-3 shrink-0 rounded-sm', STAGE_DOT[s])}
                        aria-hidden
                      />
                      <span className="flex-1">{STAGE_LABELS[s]}</span>
                      {isPending ? (
                        <Loader2
                          className="size-3.5 animate-spin text-muted-foreground"
                          aria-hidden
                        />
                      ) : isCurrent ? (
                        <Check className="size-3.5 text-muted-foreground" aria-hidden />
                      ) : isOverride && canOverride ? (
                        // F22: was ⚑ unicode glyph - replaced with a Lucide
                        // icon to match the rest of the visual system.
                        <AlertTriangle
                          className="size-3.5 text-amber-600"
                          aria-label="Override required"
                        />
                      ) : null}
                    </button>
                  </li>
                );
              })}
            </ul>
          )}
        </PopoverContent>
      </Popover>
      <AlertDialog
        open={!!openConfirmTarget}
        onOpenChange={(o) => {
          if (!o && !unlinking) setOpenConfirmTarget(null);
        }}
      >
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Reset this deal to New Enquiry?</AlertDialogTitle>
            <AlertDialogDescription>
              This interest has {linkedBerthCount} linked{' '}
              {linkedBerthCount === 1 ? 'berth' : 'berths'}. Going back to{' '}
              <strong>New Enquiry</strong> usually means restarting the lead - keeping the berth
              links would leave them showing as under offer on the public map for a deal that&apos;s
              no longer in progress.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter className="flex-col-reverse gap-2 sm:flex-row sm:justify-end">
            <AlertDialogCancel disabled={unlinking}>Cancel</AlertDialogCancel>
            <Button
              type="button"
              variant="outline"
              disabled={unlinking}
              onClick={() => openConfirmTarget && keepBerthsAndOpen(openConfirmTarget)}
            >
              Keep berth links
            </Button>
            <AlertDialogAction
              disabled={unlinking}
              onClick={(e) => {
                e.preventDefault();
                if (openConfirmTarget) void unlinkAllAndOpen(openConfirmTarget);
              }}
            >
              {unlinking && <Loader2 className="mr-1.5 size-3.5 animate-spin" aria-hidden />}
              Unlink {linkedBerthCount} {linkedBerthCount === 1 ? 'berth' : 'berths'} & reset
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </>
  );
}
