/**
 * Documents tab on the berth detail page (Phase 6b - see plan §5.6).
 *
 * Sections:
 *   - Current PDF panel (download link, "Replace PDF" button, parse-engine chip).
 *   - Version history list - newest first, with rollback affordance on every
 *     non-current row.
 *   - Reconcile-diff dialog (PdfReconcileDialog), opened after a successful
 *     upload + parse. Shows auto-applied vs conflicted fields and lets the
 *     rep accept the conflict resolution.
 *
 * The actual upload is split in two steps:
 *   1. POST /pdf-upload-url -> presigned URL + storageKey
 *   2. PUT the file to that URL (multipart for filesystem-proxy mode, signed
 *      PUT for S3 mode)
 *   3. POST /pdf-versions with the storage key + parse results
 */

'use client';

import { useRef, useState } from 'react';
import dynamic from 'next/dynamic';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import { ChevronDown, ChevronRight, Download } from 'lucide-react';

import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { PdfReconcileDialog } from './pdf-reconcile-dialog';

// pdfjs-dist is ~150kb gzip — lazy-load so the berth page only pulls it
// in when a rep actually expands the spec-sheet preview. ssr:false
// because the pdfjs worker setup needs `window`.
const PdfViewer = dynamic(
  () => import('@/components/files/pdf-viewer').then((m) => ({ default: m.PdfViewer })),
  {
    ssr: false,
    loading: () => (
      <div className="flex h-[600px] items-center justify-center text-sm text-muted-foreground">
        Loading PDF viewer…
      </div>
    ),
  },
);

interface PdfVersionRow {
  id: string;
  versionNumber: number;
  fileName: string;
  fileSizeBytes: number;
  uploadedBy: string;
  uploadedAt: string;
  isCurrent: boolean;
  downloadUrl: string;
  downloadUrlExpiresAt: string;
  parseEngine: 'acroform' | 'ocr' | 'ai' | null;
}

interface UploadUrlResponse {
  url: string;
  method: 'PUT' | 'POST';
  storageKey: string;
  maxBytes: number;
  backend: 's3' | 'filesystem';
}

export function BerthDocumentsTab({ berthId }: { berthId: string }) {
  const qc = useQueryClient();
  const fileInputRef = useRef<HTMLInputElement | null>(null);
  const [previewOpen, setPreviewOpen] = useState(true);
  const [pendingDiff, setPendingDiff] = useState<{
    versionId: string;
    autoApplied: Array<{ field: string; value: string | number }>;
    conflicts: Array<{
      field: string;
      crmValue: string | number | null;
      pdfValue: string | number | null;
      pdfConfidence: number;
    }>;
    warnings: string[];
  } | null>(null);

  const { data: versions, isLoading } = useQuery<PdfVersionRow[]>({
    queryKey: ['berth-pdf-versions', berthId],
    queryFn: () =>
      apiFetch<{ data: PdfVersionRow[] }>(`/api/v1/berths/${berthId}/pdf-versions`).then(
        (r) => r.data,
      ),
  });

  const rollback = useMutation({
    mutationFn: (versionId: string) =>
      apiFetch(`/api/v1/berths/${berthId}/pdf-versions/${versionId}/rollback`, {
        method: 'POST',
      }),
    onSuccess: () => {
      void qc.invalidateQueries({ queryKey: ['berth-pdf-versions', berthId] });
      void qc.invalidateQueries({ queryKey: ['berth', berthId] });
      toast.success('Rolled back to selected version.');
    },
    onError: (err: Error) => {
      toastError(err);
    },
  });

  const upload = useMutation({
    mutationFn: async (file: File) => {
      // 1. ask the server for a presigned upload URL
      const upRes = await apiFetch<{ data: UploadUrlResponse }>(
        `/api/v1/berths/${berthId}/pdf-upload-url`,
        {
          method: 'POST',
          body: { fileName: file.name, sizeBytes: file.size },
        },
      );
      const { url, method, storageKey, maxBytes } = upRes.data;
      if (file.size > maxBytes) {
        throw new Error(
          `File ${(file.size / 1024 / 1024).toFixed(1)} MB exceeds ${(maxBytes / 1024 / 1024).toFixed(0)} MB limit`,
        );
      }

      // 2. upload directly to storage (filesystem-proxy or S3)
      const putRes = await fetch(url, {
        method,
        body: file,
        headers: { 'content-type': 'application/pdf' },
        credentials: url.startsWith('/') ? 'include' : 'omit',
      });
      if (!putRes.ok) {
        throw new Error(`Storage PUT failed (${putRes.status})`);
      }

      // 3. compute sha256 in the browser for the metadata row
      const sha256 = await sha256Hex(file);

      // 4. register the version metadata + parse server-side. The server
      //    runs parseBerthPdf via the buffer from storage; the client
      //    doesn't ship the raw PDF a second time.
      const verRes = await apiFetch<{ data: { versionId: string } }>(
        `/api/v1/berths/${berthId}/pdf-versions`,
        {
          method: 'POST',
          body: {
            storageKey,
            fileName: file.name,
            fileSizeBytes: file.size,
            sha256,
          },
        },
      );
      return { versionId: verRes.data.versionId };
    },
    onSuccess: () => {
      void qc.invalidateQueries({ queryKey: ['berth-pdf-versions', berthId] });
      void qc.invalidateQueries({ queryKey: ['berth', berthId] });
      toast.success('PDF uploaded.');
    },
    onError: (err: Error) => {
      toastError(err);
    },
  });

  const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;
    if (!file.name.toLowerCase().endsWith('.pdf')) {
      toast.error('Only PDFs are accepted.');
      return;
    }
    upload.mutate(file);
    if (fileInputRef.current) fileInputRef.current.value = '';
  };

  const current = versions?.find((v) => v.isCurrent);
  const others = versions?.filter((v) => !v.isCurrent) ?? [];

  return (
    <div className="space-y-6">
      <p className="text-sm text-muted-foreground">
        Berth-spec PDF: the dimensional drawing or surveyor sheet for this slip. Versioned so a
        misparse can be rolled back. Deal documents (EOI, contract, etc.) live on the &ldquo;Deal
        Documents&rdquo; tab.
      </p>
      <Card>
        <CardHeader className="flex flex-row items-center justify-between pb-3">
          <CardTitle className="text-sm font-medium">Current PDF</CardTitle>
          <div>
            <input
              ref={fileInputRef}
              type="file"
              accept="application/pdf"
              className="hidden"
              onChange={onFileChange}
            />
            <Button
              size="sm"
              onClick={() => fileInputRef.current?.click()}
              disabled={upload.isPending}
            >
              {upload.isPending ? 'Uploading…' : current ? 'Replace PDF' : 'Upload PDF'}
            </Button>
          </div>
        </CardHeader>
        <CardContent className="space-y-3 pt-0 text-sm">
          {isLoading ? (
            <p className="text-muted-foreground">Loading…</p>
          ) : current ? (
            <>
              <div className="flex flex-wrap items-center gap-2">
                <button
                  type="button"
                  onClick={() => setPreviewOpen((o) => !o)}
                  className="inline-flex items-center gap-1 font-medium underline-offset-2 hover:underline"
                  aria-expanded={previewOpen}
                >
                  {previewOpen ? (
                    <ChevronDown className="size-3.5 shrink-0" aria-hidden />
                  ) : (
                    <ChevronRight className="size-3.5 shrink-0" aria-hidden />
                  )}
                  {current.fileName}
                </button>
                <span className="text-muted-foreground">
                  v{current.versionNumber} · {(current.fileSizeBytes / 1024 / 1024).toFixed(2)} MB
                </span>
                {current.parseEngine ? <ParseEngineBadge engine={current.parseEngine} /> : null}
                <a
                  href={current.downloadUrl}
                  target="_blank"
                  rel="noreferrer"
                  className="ml-auto inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground"
                >
                  <Download className="size-3.5" aria-hidden />
                  Download
                </a>
              </div>
              {previewOpen ? (
                <div className="h-[600px] overflow-hidden rounded-md border bg-muted/20">
                  <PdfViewer url={current.downloadUrl} fileName={current.fileName} />
                </div>
              ) : null}
            </>
          ) : (
            <p className="text-muted-foreground">No PDF uploaded yet.</p>
          )}
        </CardContent>
      </Card>

      <Card>
        <CardHeader className="pb-3">
          <CardTitle className="text-sm font-medium">Version history</CardTitle>
        </CardHeader>
        <CardContent className="pt-0">
          {others.length === 0 ? (
            <p className="text-sm text-muted-foreground">No prior versions.</p>
          ) : (
            <ul className="divide-y">
              {others.map((v) => (
                <li key={v.id} className="flex items-center justify-between py-2 text-sm">
                  <div>
                    <a href={v.downloadUrl} target="_blank" rel="noreferrer" className="underline">
                      {v.fileName}
                    </a>{' '}
                    <span className="text-muted-foreground">
                      v{v.versionNumber} · {(v.fileSizeBytes / 1024 / 1024).toFixed(2)} MB ·{' '}
                      {new Date(v.uploadedAt).toLocaleDateString()}
                    </span>
                  </div>
                  <Button
                    size="sm"
                    variant="outline"
                    onClick={() => rollback.mutate(v.id)}
                    disabled={rollback.isPending}
                  >
                    Rollback
                  </Button>
                </li>
              ))}
            </ul>
          )}
        </CardContent>
      </Card>

      {pendingDiff ? (
        <PdfReconcileDialog
          berthId={berthId}
          versionId={pendingDiff.versionId}
          autoApplied={pendingDiff.autoApplied}
          conflicts={pendingDiff.conflicts}
          warnings={pendingDiff.warnings}
          onClose={() => setPendingDiff(null)}
        />
      ) : null}
    </div>
  );
}

function ParseEngineBadge({ engine }: { engine: 'acroform' | 'ocr' | 'ai' }) {
  const tone = engine === 'acroform' ? 'default' : engine === 'ocr' ? 'secondary' : 'outline';
  const label = engine === 'acroform' ? 'AcroForm' : engine === 'ocr' ? 'OCR' : 'AI';
  return <Badge variant={tone}>{label}</Badge>;
}

async function sha256Hex(file: File): Promise<string> {
  const buf = await file.arrayBuffer();
  const hash = await crypto.subtle.digest('SHA-256', buf);
  return Array.from(new Uint8Array(hash))
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');
}
