'use client';

import Link from 'next/link';
import { useParams } from 'next/navigation';
import { format, formatDistanceToNowStrict } from 'date-fns';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import { Anchor, CheckCircle2, Circle, FileSignature, Plus, Send, Wallet } from 'lucide-react';

import type { DetailTab } from '@/components/shared/detail-layout';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { NotesList } from '@/components/shared/notes-list';
import { InlineEditableField } from '@/components/shared/inline-editable-field';
import { InlineTagEditor } from '@/components/shared/inline-tag-editor';
import { RecommendationList } from '@/components/interests/recommendation-list';
import { BerthRecommenderPanel } from '@/components/interests/berth-recommender-panel';
import { LinkedBerthsList } from '@/components/interests/linked-berths-list';
import { InterestTimeline } from '@/components/interests/interest-timeline';
import { InterestDocumentsTab } from '@/components/interests/interest-documents-tab';
import {
  LEAD_CATEGORIES,
  PIPELINE_STAGES,
  SOURCES,
  canTransitionStage,
  type PipelineStage,
} from '@/lib/constants';
import { InterestEoiTab } from '@/components/interests/interest-eoi-tab';
import { InterestContactLogTab } from '@/components/interests/interest-contact-log-tab';
import { InterestContractTab } from '@/components/interests/interest-contract-tab';
import { InterestReservationTab } from '@/components/interests/interest-reservation-tab';
import { useConfirmation } from '@/hooks/use-confirmation';
import { apiFetch } from '@/lib/api/client';
import { cn } from '@/lib/utils';

type InterestPatchField = 'leadCategory' | 'source';

const LEAD_CATEGORY_OPTIONS = LEAD_CATEGORIES.map((c) => ({
  value: c,
  label: c.replace(/_/g, ' ').replace(/\b\w/g, (m) => m.toUpperCase()),
}));

// Convert raw enum values like `waiting_for_signatures` → `Waiting For Signatures`.
function humanizeStatus(value: string | null): string | null {
  if (!value) return null;
  return value.replace(/_/g, ' ').replace(/\b\w/g, (m) => m.toUpperCase());
}

interface InterestTabsOptions {
  interestId: string;
  currentUserId?: string;
  /** Used by the dedicated EOI tab to deep-link to the client's record
   *  for inline edits ("wrong details? edit on the client's page"). */
  clientId?: string | null;
  interest: {
    pipelineStage: string;
    /** Drives the recommender panel mounted on the Overview tab. */
    desiredLengthFt?: string | null;
    desiredWidthFt?: string | null;
    desiredDraftFt?: string | null;
    leadCategory: string | null;
    source: string | null;
    eoiStatus: string | null;
    contractStatus: string | null;
    depositStatus: string | null;
    reservationStatus: string | null;
    dateFirstContact: string | null;
    dateLastContact: string | null;
    dateEoiSent: string | null;
    dateEoiSigned: string | null;
    dateContractSent: string | null;
    dateContractSigned: string | null;
    dateDepositReceived: string | null;
    reminderEnabled: boolean;
    reminderDays: number | null;
    reminderLastFired: string | null;
    /** Count of berths linked via the interest_berths junction —
     *  drives the "Berth Interest" milestone on the Overview tab. */
    linkedBerthCount?: number;
    notes: string | null;
    /** Surfaced by getInterestById for the Overview "most recent note"
     *  teaser - saves a click into the Notes tab to peek at the latest. */
    notesCount?: number;
    recentNote?: {
      id: string;
      content: string;
      authorId: string;
      createdAt: string;
    } | null;
    tags?: Array<{ id: string; name: string; color: string }>;
  };
}

function useInterestPatch(interestId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (patch: Partial<Record<InterestPatchField, string | null>>) =>
      apiFetch(`/api/v1/interests/${interestId}`, {
        method: 'PATCH',
        body: patch,
      }),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['interests', interestId] });
    },
  });
}

type Phase = 'past' | 'current' | 'future';

function useStageMutation(interestId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async ({
      stage,
      reason,
      override,
      milestoneDate,
    }: {
      stage: string;
      reason?: string;
      override?: boolean;
      /** Optional ISO date for the milestone column (instead of "now"). */
      milestoneDate?: string;
    }) =>
      apiFetch(`/api/v1/interests/${interestId}/stage`, {
        method: 'PATCH',
        body: { pipelineStage: stage, reason, override, milestoneDate },
      }),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['interests', interestId] });
      qc.invalidateQueries({ queryKey: ['interests'] });
    },
  });
}

function EditableRow({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div className="flex gap-2 py-1.5 border-b last:border-0 items-center">
      <dt className="w-44 shrink-0 text-sm text-muted-foreground">{label}</dt>
      <dd className="flex-1 min-w-0">{children}</dd>
    </div>
  );
}

function InfoRow({ label, value }: { label: string; value?: string | null }) {
  if (!value) return null;
  return (
    <div className="flex gap-2 py-1.5 border-b last:border-0">
      <dt className="w-44 shrink-0 text-sm text-muted-foreground">{label}</dt>
      <dd className="text-sm">{value}</dd>
    </div>
  );
}

function formatDate(date: string | null) {
  if (!date) return null;
  return format(new Date(date), 'MMM d, yyyy');
}

function relativeDate(date: string | null) {
  if (!date) return null;
  return `${formatDistanceToNowStrict(new Date(date))} ago`;
}

interface MilestoneSectionProps {
  title: string;
  icon: React.ComponentType<{ className?: string }>;
  /** Lifecycle for this milestone, in chronological order. */
  steps: Array<{
    label: string;
    date: string | null;
    /** Stage to advance to when the user clicks the action button for this step. */
    advanceStage?: string;
    /** Optional override for the action label. */
    actionLabel?: string;
    /** Suppress the inline "Mark as…" button for this step. Use when the
     *  parent supplies a richer CTA via `footer` (e.g. Deposit, where we
     *  want the invoice flow to be the primary path). */
    hideAutoButton?: boolean;
  }>;
  status: string | null;
  onAdvance: (stage: string, milestoneDate?: string) => void | Promise<void>;
  isPending: boolean;
  /** Current pipelineStage. Used to mark steps as done when the pipeline has
   *  moved past their advanceStage even if the date stamp is missing - e.g.
   *  a seed-data interest that started already at eoi_signed will show both
   *  EOI sub-steps as done. Stage truth > date truth. */
  currentStage: string;
  /** When true, this milestone is the next one the user should act on:
   *  card gets a brand-accent ring and the next-step CTA becomes a primary
   *  button. Computed by the parent based on currentStage. */
  isActive?: boolean;
  /** Extra nodes (e.g. "Create deposit invoice" link) rendered below the steps. */
  footer?: React.ReactNode;
}

/**
 * One milestone section (EOI / Deposit / Contract) - shows a vertical lifecycle
 * with completed steps checked, the next step exposing a quick "mark as…"
 * button that bumps the pipeline stage. Each stage flip auto-stamps its date
 * via the service layer (interests.service.ts). When external systems wire in
 * (Documenso webhook, paid invoice → deposit, etc.), they patch the same
 * stage endpoint and these checkmarks light up automatically.
 */
/**
 * Button that opens a date-picker popover before advancing a milestone. The
 * default is today, but the rep can back-date the event (e.g. "deposit
 * landed yesterday") so the stamped milestone column reflects the real date
 * rather than the click time.
 */
function MilestoneAdvanceButton({
  label,
  variant,
  disabled,
  onConfirm,
}: {
  label: string;
  variant: 'default' | 'outline' | 'ghostLink';
  disabled?: boolean;
  onConfirm: (milestoneDate: string) => void;
}) {
  const [open, setOpen] = useState(false);
  const [date, setDate] = useState<string>(() => new Date().toISOString().slice(0, 10));

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        {variant === 'ghostLink' ? (
          <button
            type="button"
            disabled={disabled}
            className="text-muted-foreground hover:text-foreground disabled:opacity-50"
          >
            {label}
          </button>
        ) : (
          <Button
            type="button"
            variant={variant}
            size="sm"
            disabled={disabled}
            className="mt-2 h-7 px-2.5 text-xs"
          >
            {label}
          </Button>
        )}
      </PopoverTrigger>
      <PopoverContent align="start" className="w-64 space-y-2 p-3">
        <div className="space-y-1">
          <label className="text-xs font-medium" htmlFor="milestone-date">
            Date completed
          </label>
          <Input
            id="milestone-date"
            type="date"
            value={date}
            max={new Date().toISOString().slice(0, 10)}
            onChange={(e) => setDate(e.target.value)}
            className="h-9"
          />
          <p className="text-[11px] text-muted-foreground">
            Defaults to today — back-date if the event happened earlier.
          </p>
        </div>
        <div className="flex justify-end gap-2">
          <Button type="button" variant="ghost" size="sm" onClick={() => setOpen(false)}>
            Cancel
          </Button>
          <Button
            type="button"
            size="sm"
            disabled={!date || disabled}
            onClick={() => {
              onConfirm(date);
              setOpen(false);
            }}
          >
            Confirm
          </Button>
        </div>
      </PopoverContent>
    </Popover>
  );
}

function MilestoneSection({
  title,
  icon: Icon,
  steps,
  status,
  onAdvance,
  isPending,
  currentStage,
  isActive,
  footer,
}: MilestoneSectionProps) {
  const currentStageIdx = PIPELINE_STAGES.indexOf(currentStage as PipelineStage);
  // A step counts as done if either:
  //   (a) its `advanceStage` is at or behind the current pipeline stage, OR
  //   (b) it has an explicit date stamp (from a manual mark or webhook).
  // (a) handles seeded/imported interests that arrived at a later stage
  // without per-step dates.
  const doneFlags = steps.map((step) => {
    if (step.date) return true;
    if (!step.advanceStage) return false;
    const stepIdx = PIPELINE_STAGES.indexOf(step.advanceStage as PipelineStage);
    return stepIdx !== -1 && currentStageIdx !== -1 && currentStageIdx >= stepIdx;
  });
  const firstUnsetIdx = doneFlags.findIndex((d) => !d);

  return (
    <section
      className={cn(
        'rounded-xl border bg-card p-4 shadow-sm transition-colors',
        isActive ? 'border-brand-300 bg-brand-50/40 ring-1 ring-brand-200' : 'border-border',
      )}
    >
      <header className="mb-3 flex items-center justify-between gap-2">
        <div className="flex items-center gap-2">
          <Icon className={cn('size-4', isActive ? 'text-brand-600' : 'text-muted-foreground')} />
          <h3 className="text-sm font-semibold tracking-tight text-foreground">{title}</h3>
          {isActive ? (
            <span className="rounded-full bg-brand-100 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-brand-700">
              Next
            </span>
          ) : null}
        </div>
        {status ? (
          <span className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
            {humanizeStatus(status)}
          </span>
        ) : null}
      </header>

      <ol className="space-y-2">
        {steps.map((step, i) => {
          const done = doneFlags[i] ?? false;
          const isNext = !done && i === firstUnsetIdx;
          return (
            <li key={step.label} className="flex items-start gap-2 text-sm">
              {done ? (
                <CheckCircle2 className="mt-0.5 size-4 shrink-0 text-emerald-600" />
              ) : (
                <Circle
                  className={cn(
                    'mt-0.5 size-4 shrink-0',
                    isNext ? 'text-foreground/60' : 'text-muted-foreground/40',
                  )}
                />
              )}
              <div className="min-w-0 flex-1">
                <div className="flex flex-wrap items-center gap-x-2 gap-y-0.5">
                  <span
                    className={cn(
                      'truncate',
                      done
                        ? 'text-foreground'
                        : isNext
                          ? 'text-foreground'
                          : 'text-muted-foreground',
                    )}
                  >
                    {step.label}
                  </span>
                  {step.date ? (
                    <span className="text-xs text-muted-foreground">
                      {formatDate(step.date)} · {relativeDate(step.date)}
                    </span>
                  ) : null}
                </div>
                {isNext && step.advanceStage && !step.hideAutoButton ? (
                  <MilestoneAdvanceButton
                    label={step.actionLabel ?? `Mark as ${step.label.toLowerCase()}`}
                    variant={isActive ? 'default' : 'outline'}
                    disabled={isPending}
                    onConfirm={(date) => onAdvance(step.advanceStage!, date)}
                  />
                ) : null}
              </div>
            </li>
          );
        })}
      </ol>
      {footer ? <div className="mt-3 border-t pt-3 text-xs">{footer}</div> : null}
    </section>
  );
}

/**
 * Collapsible wrapper for future-phase milestones. Hidden by default so
 * the overview stays focused on the current stage; expanding lets reps
 * record skipped milestones (the action click then routes through the
 * advance() override-confirm).
 */
function FutureMilestones({
  milestones,
  stageMutation,
  advance,
  activeMilestone,
  currentStage,
}: {
  milestones: Array<{
    key: 'berth_interest' | 'eoi' | 'deposit' | 'contract';
    title: string;
    icon: React.ComponentType<{ className?: string }>;
    status: string | null;
    steps: MilestoneSectionProps['steps'];
    footer?: React.ReactNode;
  }>;
  stageMutation: ReturnType<typeof useStageMutation>;
  advance: (stage: string) => void | Promise<void>;
  activeMilestone: 'berth_interest' | 'eoi' | 'deposit' | 'contract' | null;
  currentStage: string;
}) {
  const [expanded, setExpanded] = useState(false);
  return (
    <div className="rounded-lg border border-dashed">
      <button
        type="button"
        onClick={() => setExpanded((v) => !v)}
        className="flex w-full items-center justify-between gap-2 px-4 py-2.5 text-sm text-muted-foreground hover:text-foreground hover:bg-muted/30 transition-colors"
      >
        <span>
          {expanded ? 'Hide' : 'Show'} upcoming milestones
          <span className="ml-2 text-xs">({milestones.map((m) => m.title).join(' · ')})</span>
        </span>
        <span className="text-xs">{expanded ? '▴' : '▾'}</span>
      </button>
      {expanded && (
        <div
          className={cn(
            'grid grid-cols-1 gap-4 p-4 pt-0',
            milestones.length === 1 ? '' : 'lg:grid-cols-2',
          )}
        >
          {milestones.map((m) => (
            <MilestoneSection
              key={m.key}
              title={m.title}
              icon={m.icon}
              status={m.status}
              isPending={stageMutation.isPending}
              onAdvance={advance}
              currentStage={currentStage}
              isActive={activeMilestone === m.key}
              steps={m.steps}
              footer={m.footer}
            />
          ))}
        </div>
      )}
    </div>
  );
}

function OverviewTab({
  interestId,
  interest,
}: {
  interestId: string;
  interest: InterestTabsOptions['interest'];
}) {
  const params = useParams<{ portSlug: string }>();
  const portSlug = params?.portSlug ?? '';
  const mutation = useInterestPatch(interestId);
  const stageMutation = useStageMutation(interestId);
  const { confirm, dialog: confirmDialog } = useConfirmation();
  const save = (field: InterestPatchField) => async (next: string | null) => {
    await mutation.mutateAsync({ [field]: next });
  };
  /**
   * Advance the pipeline. When the requested target isn't a legal next
   * step (e.g. user clicked "Mark deposit received" while still on
   * EOI Sent), prompt for confirmation and pass `override:true` so the
   * backend transition guard lets the change through. Mirrors the
   * skip-ahead pattern from the inline stage picker so audit trails
   * stay consistent regardless of which surface the rep used.
   */
  const advance = async (stage: string, milestoneDate?: string) => {
    const fromStage = interest.pipelineStage as PipelineStage;
    const toStage = stage as PipelineStage;
    const isOverride = fromStage !== toStage && !canTransitionStage(fromStage, toStage);
    if (isOverride) {
      const ok = await confirm({
        title: 'Skip-ahead stage change',
        description: `This advances the stage from "${fromStage.replace(/_/g, ' ')}" to "${toStage.replace(
          /_/g,
          ' ',
        )}", which isn't a standard next step. The change will be flagged in the audit log.`,
        confirmLabel: 'Continue',
      });
      if (!ok) return;
    }
    stageMutation.mutate({
      stage,
      reason: isOverride ? 'Skip-ahead from overview milestones' : 'Marked from overview',
      override: isOverride || undefined,
      milestoneDate,
    });
  };

  // Determine each milestone's phase relative to the current pipeline
  // stage. The overview hides future-phase milestones by default — it
  // was visually noisy to see Deposit + Contract cards on a deal still
  // at the EOI stage, and the empty cards invited mis-clicks.
  //
  // Past milestones still render (collapsed history) so reps can see
  // what's been completed. Future milestones are gated behind a "Show
  // upcoming milestones" toggle so the rep CAN reach them when a deal
  // genuinely skips stages — the click then routes through the same
  // override-confirm flow as the inline stage picker.
  const stageIdx = PIPELINE_STAGES.indexOf(interest.pipelineStage as PipelineStage);
  const eoiSignedIdx = PIPELINE_STAGES.indexOf('eoi_signed');
  const depositIdx = PIPELINE_STAGES.indexOf('deposit_10pct');
  const contractSignedIdx = PIPELINE_STAGES.indexOf('contract_signed');

  const phaseFor = (milestoneEndStageIdx: number): Phase => {
    if (stageIdx === -1) return 'future';
    if (stageIdx >= milestoneEndStageIdx) return 'past';
    // The "current" milestone is the one whose end-stage hasn't been
    // reached and whose start-stage is at-or-before the current stage.
    return 'current';
  };
  // Berth Interest milestone — first thing the rep needs to capture
  // (especially for general_interest leads). Completes the moment ANY
  // berth is linked to the interest via the junction. While unset, it
  // sits as the "current" milestone unless the deal has already moved
  // past EOI sent (in which case the rep clearly didn't need a berth
  // pinned first, so we mark it 'past' implicitly).
  const hasLinkedBerth = (interest.linkedBerthCount ?? 0) > 0;
  const berthInterestPhase: Phase = hasLinkedBerth
    ? 'past'
    : stageIdx === -1 || stageIdx >= eoiSignedIdx
      ? 'past'
      : 'current';

  const eoiPhase = phaseFor(eoiSignedIdx);
  // Deposit is current once the EOI is signed but before deposit is in.
  const depositPhase: Phase =
    stageIdx === -1
      ? 'future'
      : stageIdx >= depositIdx
        ? 'past'
        : stageIdx >= eoiSignedIdx
          ? 'current'
          : 'future';
  const contractPhase: Phase =
    stageIdx === -1
      ? 'future'
      : stageIdx >= contractSignedIdx
        ? 'past'
        : stageIdx >= depositIdx
          ? 'current'
          : 'future';

  const activeMilestone: 'berth_interest' | 'eoi' | 'deposit' | 'contract' | null =
    berthInterestPhase === 'current'
      ? 'berth_interest'
      : eoiPhase === 'current'
        ? 'eoi'
        : depositPhase === 'current'
          ? 'deposit'
          : contractPhase === 'current'
            ? 'contract'
            : null;

  const toNum = (v: string | null | undefined): number | null => {
    if (v === null || v === undefined) return null;
    const n = parseFloat(v);
    return Number.isFinite(n) ? n : null;
  };

  const milestones: Array<{
    key: 'berth_interest' | 'eoi' | 'deposit' | 'contract';
    phase: Phase;
    title: string;
    icon: React.ComponentType<{ className?: string }>;
    status: string | null;
    steps: MilestoneSectionProps['steps'];
    footer?: React.ReactNode;
    /** Brief one-liner shown when the milestone is in the past-strip. */
    pastSummary: React.ReactNode;
  }> = [
    {
      key: 'berth_interest',
      phase: berthInterestPhase,
      title: 'Berth Interest',
      icon: Anchor,
      // No status badge — the count IS the status. Showing "0 berths"
      // would just duplicate the empty-state copy below.
      status: hasLinkedBerth
        ? `${interest.linkedBerthCount} berth${(interest.linkedBerthCount ?? 0) === 1 ? '' : 's'}`
        : null,
      // No advanceStage step — the milestone tracks a state (berths
      // linked) rather than a stage transition. Hide the row chrome by
      // passing an empty steps array; the footer renders the action.
      steps: [],
      footer:
        berthInterestPhase === 'current' ? (
          <div className="text-xs text-muted-foreground">
            Add a berth from the Recommendations tab or the client&apos;s active interest panel to
            mark this milestone complete.
          </div>
        ) : null,
      pastSummary: hasLinkedBerth
        ? `${interest.linkedBerthCount} berth${(interest.linkedBerthCount ?? 0) === 1 ? '' : 's'} linked`
        : 'Skipped',
    },
    {
      key: 'eoi',
      phase: eoiPhase,
      title: 'EOI',
      icon: Send,
      status: interest.eoiStatus,
      steps: [
        {
          label: 'EOI sent',
          date: interest.dateEoiSent,
          advanceStage: 'eoi_sent',
          actionLabel: 'Mark EOI as sent',
        },
        {
          label: 'EOI signed',
          date: interest.dateEoiSigned,
          advanceStage: 'eoi_signed',
          actionLabel: 'Mark EOI as signed',
        },
      ],
      pastSummary: interest.dateEoiSigned
        ? `Signed ${formatDate(interest.dateEoiSigned)}`
        : 'Completed',
    },
    {
      key: 'deposit',
      phase: depositPhase,
      title: 'Deposit',
      icon: Wallet,
      status: interest.depositStatus,
      steps: [
        {
          label: 'Deposit received',
          date: interest.dateDepositReceived,
          advanceStage: 'deposit_10pct',
          hideAutoButton: true,
        },
      ],
      footer:
        depositPhase === 'current' && !interest.dateDepositReceived ? (
          <div className="flex flex-wrap items-center gap-x-3 gap-y-1.5">
            <Button asChild size="sm" className="h-7 px-2.5 text-xs">
              <Link href={`/${portSlug}/invoices/new?interestId=${interestId}&kind=deposit`}>
                <Plus className="size-3.5" />
                Create deposit invoice
              </Link>
            </Button>
            <MilestoneAdvanceButton
              label="Mark received manually"
              variant="ghostLink"
              disabled={stageMutation.isPending}
              onConfirm={(date) => advance('deposit_10pct', date)}
            />
          </div>
        ) : null,
      pastSummary: interest.dateDepositReceived
        ? `Received ${formatDate(interest.dateDepositReceived)}`
        : 'Recorded',
    },
    {
      key: 'contract',
      phase: contractPhase,
      title: 'Contract',
      icon: FileSignature,
      status: interest.contractStatus,
      steps: [
        {
          label: 'Contract sent',
          date: interest.dateContractSent,
          advanceStage: 'contract_sent',
          actionLabel: 'Mark contract as sent',
        },
        {
          label: 'Contract signed',
          date: interest.dateContractSigned,
          advanceStage: 'contract_signed',
          actionLabel: 'Mark contract as signed',
        },
      ],
      pastSummary: interest.dateContractSigned
        ? `Signed ${formatDate(interest.dateContractSigned)}`
        : 'Completed',
    },
  ];

  const pastMilestones = milestones.filter((m) => m.phase === 'past');
  const currentMilestones = milestones.filter((m) => m.phase === 'current');
  const futureMilestones = milestones.filter((m) => m.phase === 'future');

  return (
    <div className="space-y-6">
      {/* Sales-process milestones — phase-aware so the user only sees
          what's actionable now. Past milestones collapse into a tight
          history strip; the current milestone gets the full card; future
          milestones are hidden behind a toggle so reps can still
          skip-ahead when reality calls for it (an override-confirm
          gates the actual stage move). */}
      {pastMilestones.length > 0 && (
        <div className="rounded-lg border bg-muted/20 px-4 py-2.5">
          <div className="flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-muted-foreground">
            <span className="text-[10px] font-semibold uppercase tracking-wide">Past</span>
            {pastMilestones.map((m) => (
              <span key={m.key} className="inline-flex items-center gap-1.5">
                <CheckCircle2 className="size-3 text-emerald-600" />
                <span className="font-medium text-foreground">{m.title}</span>
                <span>·</span>
                <span>{m.pastSummary}</span>
              </span>
            ))}
          </div>
        </div>
      )}

      {currentMilestones.length > 0 && (
        <div
          className={cn(
            'grid grid-cols-1 gap-4',
            currentMilestones.length === 1 ? '' : 'lg:grid-cols-2',
          )}
        >
          {currentMilestones.map((m) => (
            <MilestoneSection
              key={m.key}
              title={m.title}
              icon={m.icon}
              status={m.status}
              isPending={stageMutation.isPending}
              onAdvance={advance}
              currentStage={interest.pipelineStage}
              isActive={activeMilestone === m.key}
              steps={m.steps}
              footer={m.footer}
            />
          ))}
        </div>
      )}

      {futureMilestones.length > 0 && (
        <FutureMilestones
          milestones={futureMilestones}
          stageMutation={stageMutation}
          advance={advance}
          activeMilestone={activeMilestone}
          currentStage={interest.pipelineStage}
        />
      )}

      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
        {/* Lead & Source (editable) */}
        <div className="space-y-1">
          <h3 className="text-sm font-medium mb-2">Lead</h3>
          <dl>
            <EditableRow label="Lead Category">
              <InlineEditableField
                variant="select"
                options={LEAD_CATEGORY_OPTIONS}
                value={interest.leadCategory}
                onSave={save('leadCategory')}
              />
            </EditableRow>
            <EditableRow label="Source">
              <InlineEditableField
                variant="select"
                options={SOURCES.map((s) => ({ value: s.value, label: s.label }))}
                value={interest.source}
                onSave={save('source')}
              />
            </EditableRow>
          </dl>
        </div>

        {/* Contact dates (read-only - kept compact next to Lead) */}
        <div className="space-y-1">
          <h3 className="text-sm font-medium mb-2">Contact</h3>
          <dl>
            <InfoRow label="First Contact" value={formatDate(interest.dateFirstContact)} />
            <InfoRow label="Last Contact" value={formatDate(interest.dateLastContact)} />
            {interest.reservationStatus ? (
              <InfoRow label="Reservation" value={interest.reservationStatus} />
            ) : null}
          </dl>
        </div>

        {/* Reminder */}
        {interest.reminderEnabled && (
          <div className="space-y-1">
            <h3 className="text-sm font-medium mb-2">Reminder</h3>
            <dl>
              <InfoRow
                label="Reminder Days"
                value={interest.reminderDays ? `${interest.reminderDays} days` : null}
              />
              <InfoRow label="Last Fired" value={formatDate(interest.reminderLastFired)} />
            </dl>
          </div>
        )}

        {/* Most-recent threaded note teaser. Saves a click into the Notes
            tab when the rep just wants to peek at "what was discussed last."
            Always rendered now that the redundant `interests.notes` blob is
            gone — falls back to an empty-state prompt so reps still have an
            obvious entry point to the Notes tab from Overview. */}
        <div className="space-y-1 md:col-span-2">
          <div className="mb-2 flex items-center justify-between">
            <h3 className="text-sm font-medium">Latest note</h3>
            <Link
              href={`/${portSlug}/interests/${interestId}?tab=notes`}
              className="text-xs font-medium text-primary hover:underline"
            >
              {interest.recentNote
                ? `View all${interest.notesCount && interest.notesCount > 1 ? ` ${interest.notesCount}` : ''}`
                : 'Add note'}
            </Link>
          </div>
          {interest.recentNote ? (
            <div className="rounded-md border border-border bg-muted/30 px-3 py-2 text-sm">
              <p className="line-clamp-3 whitespace-pre-wrap text-foreground/90">
                {interest.recentNote.content}
              </p>
              <p className="mt-1 text-xs text-muted-foreground">
                {formatDistanceToNowStrict(new Date(interest.recentNote.createdAt), {
                  addSuffix: true,
                })}
                {interest.recentNote.authorId
                  ? ` · ${interest.recentNote.authorId === 'system' ? 'system' : interest.recentNote.authorId}`
                  : ''}
              </p>
            </div>
          ) : (
            <div className="rounded-md border border-dashed border-border bg-muted/10 px-3 py-2 text-xs text-muted-foreground">
              No notes yet.
            </div>
          )}
        </div>

        {/* Tags */}
        <div className="space-y-1 md:col-span-2">
          <h3 className="text-sm font-medium mb-2">Tags</h3>
          <InlineTagEditor
            endpoint={`/api/v1/interests/${interestId}/tags`}
            currentTags={interest.tags ?? []}
            invalidateKey={['interests', interestId]}
          />
        </div>
      </div>

      {/* Linked berths (plan §5.5) - shown ABOVE the recommender so reps see
          what's already linked before browsing more options. Each row exposes
          per-berth role-flag toggles and the EOI bypass control (only visible
          once the parent interest's primary EOI is signed). */}
      <LinkedBerthsList interestId={interestId} />

      {/* Berth recommender (plan §5.3) - always-mounted card driven by the
          interest's desired dimensions. Renders an inline guidance message
          when dimensions aren't set yet. */}
      <BerthRecommenderPanel
        interestId={interestId}
        desiredLengthFt={toNum(interest.desiredLengthFt)}
        desiredWidthFt={toNum(interest.desiredWidthFt)}
        desiredDraftFt={toNum(interest.desiredDraftFt)}
      />
      {confirmDialog}
    </div>
  );
}

export function getInterestTabs({
  interestId,
  currentUserId,
  clientId = null,
  interest,
}: InterestTabsOptions): DetailTab[] {
  // The EOI / Contract / Reservation tabs are stage-conditional —
  // each appears only at the stages where the rep is likely to act
  // on it. Hides clutter from later-stage deals where earlier docs
  // are ancient history. Each tab still queries for its own past
  // documents; if a deal regresses the past docs remain accessible
  // via the generic Documents tab.
  const stageIdx = PIPELINE_STAGES.indexOf(interest.pipelineStage as PipelineStage);
  const detailsSentIdx = PIPELINE_STAGES.indexOf('details_sent');
  const depositIdx = PIPELINE_STAGES.indexOf('deposit_10pct');
  const contractSignedIdx = PIPELINE_STAGES.indexOf('contract_signed');
  // EOI: from details_sent through contract_signed (the deal's whole life)
  const showEoiTab = stageIdx >= detailsSentIdx && stageIdx <= contractSignedIdx;
  // Contract: appears once the deposit's been paid (deal is committed)
  // and stays visible until the contract is signed
  const showContractTab = stageIdx >= depositIdx && stageIdx <= contractSignedIdx;
  // Reservation: appears once the contract's signed and stays visible
  // through completion (reservation is the post-contract milestone)
  const showReservationTab = stageIdx >= contractSignedIdx;

  const tabs: DetailTab[] = [
    {
      id: 'overview',
      label: 'Overview',
      content: <OverviewTab interestId={interestId} interest={interest} />,
    },
    {
      id: 'contact-log',
      label: 'Contact log',
      content: <InterestContactLogTab interestId={interestId} />,
    },
    {
      id: 'notes',
      label: 'Notes',
      content: (
        <NotesList entityType="interests" entityId={interestId} currentUserId={currentUserId} />
      ),
    },
  ];

  if (showEoiTab) {
    tabs.push({
      id: 'eoi',
      label: 'EOI',
      content: <InterestEoiTab interestId={interestId} clientId={clientId} />,
    });
  }

  if (showContractTab) {
    tabs.push({
      id: 'contract',
      label: 'Contract',
      content: <InterestContractTab interestId={interestId} clientId={clientId} />,
    });
  }

  if (showReservationTab) {
    tabs.push({
      id: 'reservation',
      label: 'Reservation',
      content: <InterestReservationTab interestId={interestId} clientId={clientId} />,
    });
  }

  tabs.push(
    {
      id: 'documents',
      label: 'Documents',
      content: <InterestDocumentsTab interestId={interestId} />,
    },
    {
      id: 'recommendations',
      label: 'Recommendations',
      content: <RecommendationList interestId={interestId} />,
    },
    {
      id: 'activity',
      label: 'Activity',
      content: <InterestTimeline interestId={interestId} />,
    },
  );

  return tabs;
}
