'use client';

import { useQueries, useQuery } from '@tanstack/react-query';
import { AlertTriangle } from 'lucide-react';
import { useParams } from 'next/navigation';

import { apiFetch } from '@/lib/api/client';
import { BerthOccupancyChip } from '@/components/berths/berth-occupancy-chip';

interface BerthRow {
  // `id` is the interest_berths JUNCTION row id; `berthId` is the actual
  // berth. The /active-interests endpoint is keyed by berth id — using `id`
  // here 404s every conflict lookup.
  id: string;
  berthId: string;
  mooringNumber: string;
  status: string;
  isPrimary: boolean;
}

interface BerthsResponse {
  data: BerthRow[];
}

interface CompetingInterest {
  interestId: string;
  clientName: string;
  pipelineStage: string;
  isPrimary: boolean;
}

/**
 * Surfaces when one of the interest's linked berths is sold or under offer
 * to a different deal. We don't block the rep from proceeding (the user
 * explicitly wanted v1 to still let the deal advance - the assumption is
 * that the rep is aware and treating the current deal as a fallback if
 * the other one falls through), but the banner makes the conflict visible
 * so they aren't surprised when the rules engine flags it.
 *
 * Fires only for active (non-archived, non-closed) interests - banners on
 * lost deals are noise.
 */
export function InterestBerthStatusBanner({
  interestId,
  interestPipelineStage,
  interestOutcome,
  archivedAt,
}: {
  interestId: string;
  interestPipelineStage: string;
  interestOutcome?: string | null;
  archivedAt?: string | null;
}) {
  const params = useParams<{ portSlug: string }>();
  const portSlug = params?.portSlug ?? '';
  const { data } = useQuery<BerthsResponse>({
    queryKey: ['interest-berths', interestId],
    queryFn: () => apiFetch(`/api/v1/interests/${interestId}/berths`),
  });

  const berths = data?.data ?? [];
  const conflicts = berths.filter((b) => b.status === 'sold' || b.status === 'under_offer');

  // Resolve the competing deal per conflicting berth via the
  // `/active-interests` endpoint shipped in 292a8b5. Filtered client-side
  // to interests OTHER THAN this one so a deal looking at its own berth
  // doesn't see itself in the banner.
  // Align query key with BerthOccupancyChip so React Query dedupes the
  // network call when the banner and the chip render side-by-side. The
  // banner does its own client-side exclude-self filter because it
  // needs the unfiltered list to decide whether to render at all.
  const competingQueries = useQueries({
    queries: conflicts.map((b) => ({
      queryKey: ['berth', b.berthId, 'active-interests'] as const,
      queryFn: () =>
        apiFetch<{ data: CompetingInterest[] }>(`/api/v1/berths/${b.berthId}/active-interests`),
      enabled: conflicts.length > 0,
      staleTime: 30_000,
    })),
  });

  if (archivedAt || interestOutcome) return null;
  // The banner is most useful before the rep is committed to the deal -
  // once contract is in motion, the conflict is moot.
  if (interestPipelineStage === 'contract') return null;

  if (conflicts.length === 0) return null;

  // Wait for every per-berth competing-interest query to finish before
  // committing to a count - otherwise the banner briefly reads "3 berths"
  // and then shrinks to "1 berth" as queries land.
  const allCompetingLoaded = competingQueries.every((q) => !q.isLoading);
  if (!allCompetingLoaded) return null;

  // A berth's status is 'under_offer' or 'sold' if ANY active interest -
  // including this one - flagged it as is_specific_interest. When this
  // interest is the only deal touching the berth, the conflict is
  // self-caused and shouldn't fire the banner: filter to berths where at
  // least one OTHER interest is in play.
  const lines = conflicts
    .map((b, idx) => {
      const q = competingQueries[idx];
      // Exclude self from the unfiltered list returned by the chip's
      // shared queryKey - banner only fires for OTHER deals.
      const otherDeals = (q?.data?.data ?? []).filter((row) => row.interestId !== interestId);
      const competing = otherDeals.find((c) => c.isPrimary) ?? otherDeals[0] ?? null;
      return { berth: b, competing };
    })
    .filter((l) => l.competing !== null);

  if (lines.length === 0) return null;

  return (
    <div
      role="status"
      className="flex items-start gap-2 rounded-md border border-rose-200 bg-rose-50 px-3 py-2 text-xs text-rose-900"
    >
      <AlertTriangle className="size-3.5 mt-0.5 shrink-0" aria-hidden />
      <div className="min-w-0">
        <p className="font-medium">
          {lines.length === 1
            ? `Berth ${lines[0]!.berth.mooringNumber} is ${
                lines[0]!.berth.status === 'sold' ? 'Sold' : 'Under Offer'
              } to another deal.`
            : `${lines.length} linked berths are no longer freely available.`}
        </p>
        <ul className="mt-1 space-y-1">
          {lines.map(({ berth, competing }) =>
            competing ? (
              <li key={berth.id} className="flex items-center gap-1.5 text-rose-900">
                <span className="font-medium shrink-0">{berth.mooringNumber}:</span>
                <BerthOccupancyChip
                  berthId={berth.berthId}
                  portSlug={portSlug}
                  excludeInterestId={interestId}
                  compact
                />
              </li>
            ) : null,
          )}
        </ul>
        <p className="mt-0.5 text-rose-800">
          You can still progress this interest as a backup, but the rep on the other deal owns the
          primary path. If their deal falls through, this one can step in.
        </p>
      </div>
    </div>
  );
}
