'use client';

import { useState } from 'react';
import { type ColumnDef } from '@tanstack/react-table';
import { MoreHorizontal, Pencil, Activity, RefreshCw, Lock } from 'lucide-react';
import { useRouter, useParams } from 'next/navigation';
import { useMutation, useQueryClient } from '@tanstack/react-query';

import { Button } from '@/components/ui/button';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { TagBadge } from '@/components/shared/tag-badge';
import { StatusPill, type StatusPillStatus } from '@/components/ui/status-pill';
import { BerthStatusQuickEdit } from './berth-status-quick-edit';
import { formatCurrency } from '@/lib/utils/currency';
import { InlineEditableField } from '@/components/shared/inline-editable-field';
import { apiFetch } from '@/lib/api/client';
import { usePermissions } from '@/hooks/use-permissions';
import { mooringLetterDot } from './mooring-letter-tone';
import { stageBadgeClass, stageLabel } from '@/lib/constants';
import { CatchUpWizard } from '@/components/berths/catch-up-wizard';
import { ActiveInterestsPopover } from '@/components/berths/active-interests-popover';

export type BerthRow = {
  id: string;
  mooringNumber: string;
  area: string | null;
  status: string;
  // Dimensions (both units; row falls back when one is null)
  lengthFt: string | null;
  widthFt: string | null;
  draftFt: string | null;
  lengthM: string | null;
  widthM: string | null;
  draftM: string | null;
  widthIsMinimum: boolean | null;
  // Capacity
  nominalBoatSize: string | null;
  nominalBoatSizeM: string | null;
  waterDepth: string | null;
  waterDepthM: string | null;
  waterDepthIsMinimum: boolean | null;
  // Pontoon details (NocoDB)
  sidePontoon: string | null;
  mooringType: string | null;
  cleatType: string | null;
  cleatCapacity: string | null;
  bollardType: string | null;
  bollardCapacity: string | null;
  access: string | null;
  bowFacing: string | null;
  berthApproved: boolean | null;
  // Power
  powerCapacity: string | null;
  voltage: string | null;
  // Pricing
  price: string | null;
  priceCurrency: string;
  weeklyRateHighUsd: string | null;
  weeklyRateLowUsd: string | null;
  dailyRateHighUsd: string | null;
  dailyRateLowUsd: string | null;
  pricingValidUntil: string | null;
  // Tenure
  tenureType: string;
  tenureYears: number | null;
  tenureStartDate: string | null;
  tenureEndDate: string | null;
  tags: Array<{ id: string; name: string; color: string }>;
  /** Most-advanced pipeline stage among the berth's active interests. Null
   *  when no active interest is linked. Read-only; computed server-side. */
  latestInterestStage?: string | null;
  /** Count of non-terminal, non-archived interests linked to this berth.
   *  Drives the "Active interests" column + the demand sort. */
  activeInterestCount?: number;
  /** #67: source of the last status write. 'manual' when a human set it
   *  via the API; 'automated' when a berth-rule fired; null on rows that
   *  haven't been touched since seed. The reconciliation surface treats
   *  'manual' + no latestInterestStage as a row needing catch-up. */
  statusOverrideMode?: string | null;
};

/**
 * Toggleable columns for the berth list ColumnPicker. Heavy NocoDB
 * fields default to hidden; reps can switch them on per-table-view.
 * `mooringNumber` is intentionally omitted from this list - it's the
 * primary identifier and always visible.
 */
export const BERTH_COLUMN_OPTIONS: Array<{ id: string; label: string }> = [
  { id: 'area', label: 'Area' },
  { id: 'status', label: 'Status' },
  { id: 'latestInterestStage', label: 'Latest deal stage' },
  { id: 'activeInterestCount', label: 'Active interests' },
  { id: 'sidePontoon', label: 'Side / Pontoon' },
  { id: 'dimensions', label: 'Dimensions' },
  { id: 'nominalBoatSize', label: 'Nominal boat size' },
  { id: 'waterDepth', label: 'Water depth' },
  { id: 'mooringType', label: 'Mooring type' },
  { id: 'cleat', label: 'Cleat (type · capacity)' },
  { id: 'bollard', label: 'Bollard (type · capacity)' },
  { id: 'access', label: 'Access' },
  { id: 'bowFacing', label: 'Bow facing' },
  { id: 'berthApproved', label: 'Approved' },
  { id: 'power', label: 'Power (kW · V)' },
  { id: 'price', label: 'Price' },
  { id: 'rates', label: 'Daily / Weekly rates' },
  { id: 'pricingValidUntil', label: 'Pricing valid until' },
  { id: 'tenure', label: 'Tenure' },
  { id: 'tags', label: 'Tags' },
];

/** Hidden by default - power-users turn them on via the picker. */
export const BERTH_DEFAULT_HIDDEN: string[] = [
  'tenure',
  'sidePontoon',
  'nominalBoatSize',
  'waterDepth',
  'mooringType',
  'cleat',
  'bollard',
  'access',
  'bowFacing',
  'berthApproved',
  'power',
  'rates',
  'pricingValidUntil',
];

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

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

function StatusBadge({ status }: { status: string }) {
  return (
    <StatusPill status={BERTH_STATUS_PILL[status] ?? 'pending'}>
      {BERTH_STATUS_LABELS[status] ?? status}
    </StatusPill>
  );
}

/**
 * Chip beside the status pill flagging a manually-pinned status (wins over
 * automatic derivation). Two variants:
 *   - 'catchup' (amber): manual AND no backing interest - a candidate for the
 *     catch-up wizard (rep flipped to Under Offer/Sold without a matching deal).
 *   - 'pinned' (slate + lock): manual WITH a backing deal - a deliberate pin.
 */
function ManualBadge({ variant }: { variant: 'catchup' | 'pinned' }) {
  if (variant === 'catchup') {
    return (
      <span
        className="inline-flex items-center rounded-full border border-amber-300 bg-amber-50 px-1.5 py-0.5 text-xs font-medium uppercase tracking-wide text-amber-800"
        title="Status set manually with no backing interest - needs catch-up"
      >
        Manual
      </span>
    );
  }
  return (
    <span
      className="inline-flex items-center gap-1 rounded-full border border-slate-300 bg-slate-100 px-1.5 py-0.5 text-xs font-medium text-slate-700"
      title="Status pinned manually - wins over automatic derivation"
    >
      <Lock className="h-3 w-3" aria-hidden />
      Manual
    </span>
  );
}

function ActionsCell({ row }: { row: { original: BerthRow } }) {
  const router = useRouter();
  const params = useParams<{ portSlug: string }>();
  const berth = row.original;
  const [catchUpOpen, setCatchUpOpen] = useState(false);
  const isManualUnreconciled = berth.statusOverrideMode === 'manual' && !berth.latestInterestStage;

  return (
    <>
      <DropdownMenu>
        <DropdownMenuTrigger asChild>
          <Button
            variant="ghost"
            size="icon"
            className="h-8 w-8"
            aria-label={`Row actions for berth ${berth.mooringNumber ?? ''}`.trim()}
            onClick={(e) => e.stopPropagation()}
          >
            <MoreHorizontal className="h-4 w-4" aria-hidden />
            <span className="sr-only">Open menu</span>
          </Button>
        </DropdownMenuTrigger>
        <DropdownMenuContent align="end">
          <DropdownMenuItem
            onClick={(e) => {
              e.stopPropagation();
              router.push(`/${params.portSlug}/berths/${berth.id}`);
            }}
          >
            <Activity className="mr-2 h-4 w-4" aria-hidden />
            View details
          </DropdownMenuItem>
          <DropdownMenuItem
            onClick={(e) => {
              e.stopPropagation();
              router.push(`/${params.portSlug}/berths/${berth.id}?edit=true`);
            }}
          >
            <Pencil className="mr-2 h-4 w-4" aria-hidden />
            Edit
          </DropdownMenuItem>
          {isManualUnreconciled ? (
            <DropdownMenuItem
              onClick={(e) => {
                e.stopPropagation();
                setCatchUpOpen(true);
              }}
            >
              <RefreshCw className="mr-2 h-4 w-4" aria-hidden />
              Catch up…
            </DropdownMenuItem>
          ) : null}
        </DropdownMenuContent>
      </DropdownMenu>
      {isManualUnreconciled ? (
        <CatchUpWizard
          berthId={catchUpOpen ? berth.id : null}
          open={catchUpOpen}
          onOpenChange={setCatchUpOpen}
        />
      ) : null}
    </>
  );
}

function ActiveInterestsCell({ berthId, count }: { berthId: string; count: number }) {
  const params = useParams<{ portSlug: string }>();
  const portSlug = params?.portSlug ?? '';
  return <ActiveInterestsPopover berthId={berthId} portSlug={portSlug} count={count} />;
}

/**
 * Price column cell. Reps with the `berths.update_prices` permission get
 * a click-to-edit inline field — saves go through the focused price-only
 * route so non-`edit` roles can retune pricing without unlocking the rest
 * of the berth schema. Click stops bubbling so the row's navigate-to-
 * detail handler doesn't fire while the rep is editing.
 */
function PriceCell({
  berthId,
  price,
  currency,
}: {
  berthId: string;
  price: string | null;
  currency: string;
}) {
  const { can } = usePermissions();
  const qc = useQueryClient();
  const display = price ? (formatCurrency(price, currency, { maxFractionDigits: 0 }) ?? '-') : null;

  const mutation = useMutation({
    mutationFn: async (next: number | null) =>
      apiFetch(`/api/v1/berths/${berthId}/price`, {
        method: 'PATCH',
        body: { price: next },
      }),
    onSuccess: () => {
      void qc.invalidateQueries({ queryKey: ['berths'] });
    },
  });

  if (!can('berths', 'update_prices')) {
    return <span>{display ?? '-'}</span>;
  }

  return (
    <div onClick={(e) => e.stopPropagation()} className="inline-flex">
      <InlineEditableField
        value={price ?? null}
        displayValue={display}
        emptyText="-"
        placeholder="Enter price"
        onSave={async (next) => {
          const parsed = next === null || next.trim() === '' ? null : Number(next);
          if (parsed !== null && (!Number.isFinite(parsed) || parsed < 0)) {
            throw new Error('Price must be a positive number');
          }
          await mutation.mutateAsync(parsed);
        }}
      />
    </div>
  );
}

function joinNonNull(parts: Array<string | null | undefined>, sep = ' · '): string {
  return parts.filter((p): p is string => Boolean(p)).join(sep);
}

/**
 * Static column list rendered in metric units (the historical default).
 * Most callers should use `getBerthColumns(unit)` instead, which lets the
 * berth-list toolbar toggle render imperial when the rep prefers feet.
 */
export const berthColumns: ColumnDef<BerthRow, unknown>[] = [
  {
    accessorKey: 'mooringNumber',
    header: 'Mooring #',
    cell: ({ row }) => {
      const dot = mooringLetterDot(row.original.mooringNumber);
      return (
        <span className="inline-flex items-center gap-2 font-medium">
          {dot && <span className={`inline-block size-2 rounded-full ${dot}`} aria-hidden />}
          {row.original.mooringNumber}
        </span>
      );
    },
  },
  {
    id: 'area',
    accessorKey: 'area',
    header: 'Area',
    cell: ({ row }) => row.original.area ?? '-',
  },
  {
    id: 'status',
    accessorKey: 'status',
    header: 'Status',
    cell: ({ row }) => {
      const r = row.original;
      const isManual = r.statusOverrideMode === 'manual';
      const isManualUnreconciled = isManual && !r.latestInterestStage;
      return (
        <div className="inline-flex items-center gap-1.5">
          <BerthStatusQuickEdit berthId={r.id} currentStatus={r.status}>
            <StatusBadge status={r.status} />
          </BerthStatusQuickEdit>
          {isManual ? <ManualBadge variant={isManualUnreconciled ? 'catchup' : 'pinned'} /> : null}
        </div>
      );
    },
  },
  {
    id: 'latestInterestStage',
    header: 'Latest deal stage',
    enableSorting: true,
    cell: ({ row }) => {
      const s = row.original.latestInterestStage;
      if (!s) return <span className="text-muted-foreground">-</span>;
      return (
        <span
          className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${stageBadgeClass(s)}`}
        >
          {stageLabel(s)}
        </span>
      );
    },
  },
  {
    id: 'activeInterestCount',
    accessorKey: 'activeInterestCount',
    header: 'Active interests',
    cell: ({ row }) => (
      <ActiveInterestsCell
        berthId={row.original.id}
        count={row.original.activeInterestCount ?? 0}
      />
    ),
  },
  {
    id: 'sidePontoon',
    header: 'Side / Pontoon',
    enableSorting: false,
    cell: ({ row }) => row.original.sidePontoon ?? '-',
  },
  {
    id: 'dimensions',
    header: 'Dimensions',
    enableSorting: false,
    cell: ({ row }) => {
      const { lengthM, widthM, draftM, widthIsMinimum } = row.original;
      if (!lengthM && !widthM) return '-';
      const widthLabel = widthM ? `${widthIsMinimum ? '≥' : ''}${widthM}m` : '?';
      const base = `${lengthM ?? '?'}m × ${widthLabel}`;
      return draftM ? `${base} (draft ${draftM}m)` : base;
    },
  },
  {
    id: 'nominalBoatSize',
    header: 'Boat size',
    enableSorting: false,
    cell: ({ row }) => {
      const m = row.original.nominalBoatSizeM;
      const ft = row.original.nominalBoatSize;
      if (!m && !ft) return '-';
      return m ? `${m}m` : `${ft}ft`;
    },
  },
  {
    id: 'waterDepth',
    header: 'Water depth',
    enableSorting: false,
    cell: ({ row }) => {
      const { waterDepthM, waterDepthIsMinimum } = row.original;
      if (!waterDepthM) return '-';
      return `${waterDepthIsMinimum ? '≥' : ''}${waterDepthM}m`;
    },
  },
  {
    id: 'mooringType',
    header: 'Mooring type',
    enableSorting: false,
    cell: ({ row }) => row.original.mooringType ?? '-',
  },
  {
    id: 'cleat',
    header: 'Cleat',
    enableSorting: false,
    cell: ({ row }) => joinNonNull([row.original.cleatType, row.original.cleatCapacity]) || '-',
  },
  {
    id: 'bollard',
    header: 'Bollard',
    enableSorting: false,
    cell: ({ row }) => joinNonNull([row.original.bollardType, row.original.bollardCapacity]) || '-',
  },
  {
    id: 'access',
    header: 'Access',
    enableSorting: false,
    cell: ({ row }) => row.original.access ?? '-',
  },
  {
    id: 'bowFacing',
    header: 'Bow facing',
    enableSorting: false,
    cell: ({ row }) => row.original.bowFacing ?? '-',
  },
  {
    id: 'berthApproved',
    header: 'Approved',
    enableSorting: false,
    cell: ({ row }) => (row.original.berthApproved ? 'Yes' : 'No'),
  },
  {
    id: 'power',
    header: 'Power',
    enableSorting: false,
    cell: ({ row }) => {
      const kw = row.original.powerCapacity;
      const v = row.original.voltage;
      if (!kw && !v) return '-';
      return joinNonNull([kw ? `${kw}kW` : null, v ? `${v}V` : null]);
    },
  },
  {
    id: 'price',
    accessorKey: 'price',
    header: 'Price',
    cell: ({ row }) => (
      <PriceCell
        berthId={row.original.id}
        price={row.original.price}
        currency={row.original.priceCurrency}
      />
    ),
  },
  {
    id: 'rates',
    header: 'Rates (USD)',
    enableSorting: false,
    cell: ({ row }) => {
      const { dailyRateLowUsd, dailyRateHighUsd, weeklyRateLowUsd, weeklyRateHighUsd } =
        row.original;
      const daily =
        dailyRateLowUsd && dailyRateHighUsd
          ? `${dailyRateLowUsd}–${dailyRateHighUsd}/d`
          : dailyRateLowUsd
            ? `${dailyRateLowUsd}/d`
            : null;
      const weekly =
        weeklyRateLowUsd && weeklyRateHighUsd
          ? `${weeklyRateLowUsd}–${weeklyRateHighUsd}/wk`
          : weeklyRateLowUsd
            ? `${weeklyRateLowUsd}/wk`
            : null;
      return joinNonNull([daily, weekly]) || '-';
    },
  },
  {
    id: 'pricingValidUntil',
    header: 'Pricing valid',
    enableSorting: false,
    cell: ({ row }) => row.original.pricingValidUntil ?? '-',
  },
  {
    id: 'tenure',
    accessorKey: 'tenureType',
    header: 'Tenure',
    cell: ({ row }) => (row.original.tenureType === 'permanent' ? 'Permanent' : 'Fixed Term'),
  },
  {
    id: 'tags',
    header: 'Tags',
    enableSorting: false,
    cell: ({ row }) => {
      const { tags } = row.original;
      if (!tags || tags.length === 0) return null;
      return (
        <div className="flex flex-wrap gap-1">
          {tags.slice(0, 3).map((tag) => (
            <TagBadge key={tag.id} name={tag.name} color={tag.color} />
          ))}
          {tags.length > 3 && (
            <span className="text-xs text-muted-foreground">+{tags.length - 3}</span>
          )}
        </div>
      );
    },
  },
  {
    id: 'actions',
    header: '',
    enableSorting: false,
    size: 48,
    cell: ({ row }) => <ActionsCell row={row} />,
  },
];

/**
 * Returns a copy of `berthColumns` with the dimension-bearing cells
 * rewritten to render in the requested unit. Used by `BerthList` so the
 * column-header toggle can flip the rendering globally without each
 * cell renderer reading a context.
 *
 * Imperial columns assume the canonical `*Ft` columns are populated
 * (true by default - the import pipeline + bulk-add wizard write both,
 * and the inline editor in yacht-tabs.tsx auto-fills the counterpart).
 * Rows with only the metric counterpart fall through to `?` for that
 * dimension; the cell still renders so the rep sees what's set.
 */
export function getBerthColumns(unit: 'ft' | 'm'): ColumnDef<BerthRow, unknown>[] {
  if (unit === 'm') return berthColumns;
  return berthColumns.map((col) => {
    if (col.id === 'dimensions') {
      return {
        ...col,
        cell: ({ row }) => {
          const { lengthFt, widthFt, draftFt, widthIsMinimum } = row.original;
          if (!lengthFt && !widthFt) return '-';
          const widthLabel = widthFt ? `${widthIsMinimum ? '≥' : ''}${widthFt}ft` : '?';
          const base = `${lengthFt ?? '?'}ft × ${widthLabel}`;
          return draftFt ? `${base} (draft ${draftFt}ft)` : base;
        },
      };
    }
    if (col.id === 'nominalBoatSize') {
      return {
        ...col,
        cell: ({ row }) => {
          const ft = row.original.nominalBoatSize;
          const m = row.original.nominalBoatSizeM;
          if (!ft && !m) return '-';
          return ft ? `${ft}ft` : `${m}m`;
        },
      };
    }
    if (col.id === 'waterDepth') {
      // Water depth lacks a stored `*Ft` column today; convert from meters
      // on the fly when the rep prefers ft. 1m = 3.2808ft (canonical
      // ratio used in yacht-dimensions.ts).
      return {
        ...col,
        cell: ({ row }) => {
          const { waterDepthM, waterDepthIsMinimum } = row.original;
          if (!waterDepthM) return '-';
          const ft = Number(waterDepthM) * 3.2808;
          return `${waterDepthIsMinimum ? '≥' : ''}${ft.toFixed(1)}ft`;
        },
      };
    }
    return col;
  });
}
