'use client';

import { useState } from 'react';
import { Check, ChevronsUpDown, Lock, Pencil, RefreshCw, RotateCcw } from 'lucide-react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';

import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogFooter,
} from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
import { DetailHeaderStrip } from '@/components/shared/detail-header-strip';
import { FormErrorSummary } from '@/components/forms/form-error-summary';
import { useFormScrollToError } from '@/hooks/use-form-scroll-to-error';
import { PermissionGate } from '@/components/shared/permission-gate';
import { StatusPill, type StatusPillStatus } from '@/components/ui/status-pill';
import { BerthForm } from './berth-form';
import { mooringLetterDot } from './mooring-letter-tone';
import { cn } from '@/lib/utils';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import { useVocabulary } from '@/hooks/use-vocabulary';
import { updateBerthStatusSchema, type UpdateBerthStatusInput } from '@/lib/validators/berths';
import { BERTH_STATUSES, stageBadgeClass, stageDotClass, stageLabel } from '@/lib/constants';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from '@/components/ui/command';

export type BerthDetailData = {
  id: string;
  mooringNumber: string;
  area: string | null;
  status: string;
  portId: string;
  lengthFt: string | null;
  lengthM: string | null;
  widthFt: string | null;
  widthM: string | null;
  draftFt: string | null;
  draftM: string | null;
  widthIsMinimum: boolean | null;
  nominalBoatSize: string | null;
  nominalBoatSizeM: string | null;
  waterDepth: string | null;
  waterDepthM: string | null;
  waterDepthIsMinimum: boolean | null;
  sidePontoon: string | null;
  cleatType: string | null;
  cleatCapacity: string | null;
  bollardType: string | null;
  bollardCapacity: string | null;
  bowFacing: string | null;
  price: string | null;
  priceCurrency: string;
  tenureType: string;
  tenureYears: number | null;
  tenureStartDate: string | null;
  tenureEndDate: string | null;
  powerCapacity: string | null;
  voltage: string | null;
  mooringType: string | null;
  access: string | null;
  berthApproved: boolean | null;
  statusLastChangedReason: string | null;
  statusLastModified: string | null;
  /** 'manual' = a human pinned this status (wins over derived signals);
   *  'automated' = set by the rules engine; null = pure derived. */
  statusOverrideMode: string | null;
  /** Pipeline stage of the most recent active interest on this berth, if any.
   *  Used to flag a manual pin that diverges from an active deal. */
  latestInterestStage: string | null;
  tags: Array<{ id: string; name: string; color: string }>;
};

interface BerthDetailHeaderProps {
  berth: BerthDetailData;
}

const STATUS_LABELS: Record<string, string> = {
  available: 'Available',
  under_offer: 'Under Offer',
  sold: 'Sold',
};

const BERTH_STATUS_PILL: Record<string, StatusPillStatus> = {
  available: 'available',
  under_offer: 'under_offer',
  sold: 'sold',
};

interface InterestOption {
  id: string;
  clientName: string;
  pipelineStage: string;
  /** Used to sort the picker - most recently interacted with floats to the top. */
  updatedAt?: string;
}

function StatusChangeDialog({
  berthId,
  currentStatus,
  open,
  onOpenChange,
}: {
  berthId: string;
  currentStatus: string;
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const queryClient = useQueryClient();
  const reasonChips = useVocabulary('berth_status_change_reasons');
  const {
    register,
    handleSubmit,
    setValue,
    watch,
    reset,
    formState: { errors, isSubmitting },
  } = useForm<UpdateBerthStatusInput>({
    resolver: zodResolver(updateBerthStatusSchema),
    defaultValues: { status: currentStatus as (typeof BERTH_STATUSES)[number], reason: '' },
  });
  const submitWithScroll = useFormScrollToError(handleSubmit, errors);

  const status = watch('status');
  const interestId = watch('interestId');
  const showInterestPicker = status === 'under_offer' || status === 'sold';

  // Active interests for this port - used to populate the prospect
  // selector when status moves to under_offer / sold. Only fetched when
  // the picker is actually visible to avoid an unnecessary round-trip
  // for available-status changes.
  const interestsQuery = useQuery<{
    data: Array<{
      id: string;
      clientName: string;
      pipelineStage: string;
      updatedAt?: string;
    }>;
  }>({
    queryKey: ['interests', 'status-link-picker'],
    queryFn: () => apiFetch('/api/v1/interests?pageSize=200&sort=updatedAt&order=desc'),
    enabled: open && showInterestPicker,
    staleTime: 60_000,
  });
  const interestOptions: InterestOption[] = interestsQuery.data?.data ?? [];

  async function onSubmit(data: UpdateBerthStatusInput) {
    try {
      await apiFetch(`/api/v1/berths/${berthId}/status`, {
        method: 'PATCH',
        body: data,
      });
      queryClient.invalidateQueries({ queryKey: ['berths'] });
      queryClient.invalidateQueries({ queryKey: ['berth', berthId] });
      queryClient.invalidateQueries({ queryKey: ['interests'] });
      toast.success('Status updated');
      reset();
      onOpenChange(false);
    } catch (err: unknown) {
      toastError(err);
    }
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-md">
        <DialogHeader>
          <DialogTitle>Change Status</DialogTitle>
        </DialogHeader>
        <form onSubmit={submitWithScroll(onSubmit)} className="space-y-4">
          <FormErrorSummary
            errors={errors}
            labels={{ status: 'Status', reason: 'Reason', interestId: 'Linked interest' }}
          />
          <div className="space-y-2">
            <Label>New Status</Label>
            <Select
              value={status}
              onValueChange={(v) => {
                setValue('status', v as (typeof BERTH_STATUSES)[number]);
                // Clear the interest pick when moving back to available so
                // a stale value doesn't sneak through on submit.
                if (v === 'available') setValue('interestId', undefined);
              }}
            >
              <SelectTrigger>
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                {BERTH_STATUSES.map((s) => (
                  <SelectItem key={s} value={s}>
                    {STATUS_LABELS[s]}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
          <div className="space-y-2">
            <Label>Reason *</Label>
            {reasonChips.length > 0 && (
              <div className="flex flex-wrap gap-1.5">
                {reasonChips.map((chip) => (
                  <button
                    type="button"
                    key={chip}
                    onClick={() => setValue('reason', chip, { shouldDirty: true })}
                    className="rounded-full border border-muted-foreground/20 bg-muted px-2.5 py-0.5 text-xs hover:bg-accent"
                  >
                    {chip}
                  </button>
                ))}
              </div>
            )}
            <Textarea {...register('reason')} placeholder="Reason for status change..." rows={3} />
          </div>
          {showInterestPicker && (
            <div className="space-y-2">
              <Label>Linked interest (optional)</Label>
              <InterestLinkPicker
                value={interestId ?? null}
                options={interestOptions}
                onChange={(id) => setValue('interestId', id ?? undefined)}
              />
              <p className="text-xs text-muted-foreground">
                Link this status change to the interest it relates to. The change will appear on
                that interest&apos;s timeline, and the berth gets attached to it automatically if it
                wasn&apos;t already.
              </p>
            </div>
          )}
          <DialogFooter>
            <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
              Cancel
            </Button>
            <Button type="submit" disabled={isSubmitting}>
              {isSubmitting ? 'Saving…' : 'Update Status'}
            </Button>
          </DialogFooter>
        </form>
      </DialogContent>
    </Dialog>
  );
}

/**
 * Confirm dialog for "Reset to automatic" - drops the manual pin so the berth
 * resumes derived status. A reason is required (audit trail). An optional
 * `defaultReason` pre-fills the box for the one-click banner path.
 */
function ResetOverrideDialog({
  berthId,
  open,
  onOpenChange,
  defaultReason = '',
}: {
  berthId: string;
  open: boolean;
  onOpenChange: (open: boolean) => void;
  defaultReason?: string;
}) {
  const queryClient = useQueryClient();
  const [reason, setReason] = useState(defaultReason);

  const resetMutation = useMutation({
    mutationFn: () =>
      apiFetch(`/api/v1/berths/${berthId}/status/reset`, {
        method: 'POST',
        body: { reason: reason.trim() || 'Resumed automatic status' },
      }),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['berths'] });
      queryClient.invalidateQueries({ queryKey: ['berth', berthId] });
      toast.success('Berth reset to automatic status');
      onOpenChange(false);
    },
    onError: (err) => toastError(err),
  });

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-md">
        <DialogHeader>
          <DialogTitle>Reset to automatic status</DialogTitle>
        </DialogHeader>
        <div className="space-y-3">
          <p className="text-sm text-muted-foreground">
            This removes the manual pin so the berth&apos;s public status is governed by its
            interests and tenancies again. The displayed status may change immediately.
          </p>
          <div className="space-y-2">
            <Label>Reason *</Label>
            <Textarea
              value={reason}
              onChange={(e) => setReason(e.target.value)}
              placeholder="Why are you dropping the manual pin?"
              rows={3}
            />
          </div>
        </div>
        <DialogFooter className="gap-2">
          <Button variant="outline" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            onClick={() => resetMutation.mutate()}
            disabled={resetMutation.isPending || reason.trim().length === 0}
          >
            Reset to automatic
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

export function BerthDetailHeader({ berth }: BerthDetailHeaderProps) {
  const [editOpen, setEditOpen] = useState(false);
  const [statusOpen, setStatusOpen] = useState(false);
  const [resetOpen, setResetOpen] = useState(false);

  const isManualPin = berth.statusOverrideMode === 'manual';
  // Divergence the rep should resolve: a berth manually pinned "Available"
  // that nevertheless has an active deal on it - the public map shows
  // Available even though something is happening. Prompt to resume automatic
  // (which derives "Under Offer" from the active specific-interest link).
  const pinDivergesFromDeal =
    isManualPin && berth.status === 'available' && !!berth.latestInterestStage;

  return (
    <>
      <DetailHeaderStrip>
        {/* Stacks vertically on phone widths so the action buttons don't
            squeeze the area subtitle into a two-line wrap. From sm up the
            title/area block sits side-by-side with the action buttons. */}
        <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-4">
          <div className="flex-1 min-w-0 flex items-center gap-3 flex-wrap">
            {/* Compact mooring chip  -  the mooring number sits inside a
                rounded plate tinted by the mooring-letter palette (same
                colour used for the row-accent in the berth list). The
                redundant "B Dock" tag from the previous design is replaced
                with a title attribute so the area only surfaces on hover,
                keeping the header lean. */}
            <div
              className={cn(
                'inline-flex h-12 min-w-13 items-center justify-center rounded-2xl px-3 text-lg font-bold tracking-tight text-white shadow-sm',
                mooringLetterDot(berth.mooringNumber) ?? 'bg-slate-400',
              )}
              title={berth.area ? `${berth.area} Dock` : undefined}
              aria-label={`Berth ${berth.mooringNumber}${berth.area ? `, ${berth.area} Dock` : ''}`}
            >
              {berth.mooringNumber}
            </div>
            <StatusPill
              status={BERTH_STATUS_PILL[berth.status] ?? 'pending'}
              className="px-3 py-1 text-sm"
            >
              {STATUS_LABELS[berth.status] ?? berth.status}
            </StatusPill>
            {isManualPin && (
              <span
                className="inline-flex items-center gap-1 rounded-full border border-slate-300 bg-slate-100 px-2 py-0.5 text-xs font-medium text-slate-700"
                title={
                  berth.statusLastChangedReason
                    ? `Manually set status (wins over automatic). Reason: ${berth.statusLastChangedReason}`
                    : 'Manually set status - wins over automatic derivation'
                }
              >
                <Lock className="h-3 w-3" aria-hidden />
                Manual
              </span>
            )}
          </div>

          <div className="flex flex-wrap items-center gap-2 sm:shrink-0">
            <PermissionGate resource="berths" action="edit">
              <Button variant="outline" size="sm" onClick={() => setStatusOpen(true)}>
                <RefreshCw className="mr-1.5 h-4 w-4" aria-hidden />
                Change Status
              </Button>
              {isManualPin && (
                <Button variant="outline" size="sm" onClick={() => setResetOpen(true)}>
                  <RotateCcw className="mr-1.5 h-4 w-4" aria-hidden />
                  Reset to automatic
                </Button>
              )}
              <Button size="sm" onClick={() => setEditOpen(true)}>
                <Pencil className="mr-1.5 h-4 w-4" aria-hidden />
                Edit
              </Button>
            </PermissionGate>
          </div>
        </div>
      </DetailHeaderStrip>

      {pinDivergesFromDeal && (
        <PermissionGate resource="berths" action="edit">
          <div className="mt-2 flex flex-col gap-2 rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-900 sm:flex-row sm:items-center sm:justify-between">
            <span>
              This berth is manually pinned <strong>Available</strong>, but it has an active deal.
              The public map will keep showing Available until you reset it.
            </span>
            <Button
              size="sm"
              variant="outline"
              className="shrink-0 border-amber-400 bg-white hover:bg-amber-100"
              onClick={() => setResetOpen(true)}
            >
              <RotateCcw className="mr-1.5 h-4 w-4" aria-hidden />
              Reset to automatic
            </Button>
          </div>
        </PermissionGate>
      )}

      <BerthForm berth={berth} open={editOpen} onOpenChange={setEditOpen} />

      <StatusChangeDialog
        berthId={berth.id}
        currentStatus={berth.status}
        open={statusOpen}
        onOpenChange={setStatusOpen}
      />

      <ResetOverrideDialog
        berthId={berth.id}
        open={resetOpen}
        onOpenChange={setResetOpen}
        defaultReason={pinDivergesFromDeal ? 'Resumed automatic status - active deal present' : ''}
      />
    </>
  );
}

/**
 * Searchable combobox for picking a linked prospect when changing berth
 * status. Replaces the bare Select which had no filter, no stage colours,
 * and no recency sort - for ports with 200+ active interests that became
 * a scroll-fest. Stage labels render with the same coloured pill the rest
 * of the CRM uses for stage badges so the rep can scan the list visually.
 */
function InterestLinkPicker({
  value,
  options,
  onChange,
}: {
  value: string | null;
  options: InterestOption[];
  onChange: (id: string | null) => void;
}) {
  const [open, setOpen] = useState(false);
  // Sort with the most recently updated interest first so reps see the
  // active deals at the top of the list - older / dormant ones drop
  // beneath. `updatedAt` is set on every patch + every stage advance.
  const sorted = [...options].sort((a, b) => {
    if (!a.updatedAt && !b.updatedAt) return 0;
    if (!a.updatedAt) return 1;
    if (!b.updatedAt) return -1;
    return b.updatedAt.localeCompare(a.updatedAt);
  });
  const selected = value ? sorted.find((o) => o.id === value) : null;

  return (
    <Popover open={open} onOpenChange={setOpen} modal>
      <PopoverTrigger asChild>
        <Button
          type="button"
          variant="outline"
          role="combobox"
          aria-expanded={open}
          className={cn('w-full justify-between font-normal', !value && 'text-muted-foreground')}
        >
          {selected ? (
            <span className="flex min-w-0 items-center gap-2">
              <span
                className={cn(
                  'inline-block h-2 w-2 shrink-0 rounded-full',
                  stageDotClass(selected.pipelineStage),
                )}
                aria-hidden
              />
              <span className="truncate">{selected.clientName}</span>
              <span className="ml-1 shrink-0 text-xs text-muted-foreground">
                · {stageLabel(selected.pipelineStage)}
              </span>
            </span>
          ) : (
            ' -  No interest  - '
          )}
          <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" aria-hidden />
        </Button>
      </PopoverTrigger>
      <PopoverContent className="w-(--radix-popper-anchor-width) min-w-[320px] p-0" align="start">
        <Command>
          <CommandInput placeholder="Search prospects…" />
          <CommandList>
            <CommandEmpty>No prospects found.</CommandEmpty>
            <CommandGroup>
              <CommandItem
                value="__none__"
                onSelect={() => {
                  onChange(null);
                  setOpen(false);
                }}
                className="text-muted-foreground"
              >
                - No interest -
              </CommandItem>
            </CommandGroup>
            <CommandGroup heading="Most recent first">
              {sorted.map((opt) => (
                <CommandItem
                  // cmdk filters by `value`; include client name + stage so the
                  // search input matches both. Falling back to id keeps the
                  // option selectable even if a name is blank.
                  key={opt.id}
                  value={`${opt.clientName} ${stageLabel(opt.pipelineStage)} ${opt.id}`}
                  onSelect={() => {
                    onChange(opt.id);
                    setOpen(false);
                  }}
                  className="flex items-center gap-2"
                >
                  <span
                    className={cn(
                      'inline-block h-2 w-2 shrink-0 rounded-full',
                      stageDotClass(opt.pipelineStage),
                    )}
                    aria-hidden
                  />
                  <span className="flex-1 truncate">{opt.clientName || '(unnamed)'}</span>
                  <span
                    className={cn(
                      'inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium',
                      stageBadgeClass(opt.pipelineStage),
                    )}
                  >
                    {stageLabel(opt.pipelineStage)}
                  </span>
                  {value === opt.id ? (
                    <Check className="h-3.5 w-3.5 text-muted-foreground" aria-hidden />
                  ) : null}
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}
