'use client';

import 'react-image-crop/dist/ReactCrop.css';

import { useEffect, useRef, useState } from 'react';
import ReactCrop, { centerCrop, makeAspectCrop, type Crop, type PixelCrop } from 'react-image-crop';
import { Loader2, RefreshCw, Trash2, Upload } from 'lucide-react';

import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import { toastError } from '@/lib/api/toast-error';
import { resolvePortIdFromSlug } from '@/lib/api/client';
import { toast } from 'sonner';
import { useConfirmation } from '@/hooks/use-confirmation';

const ACCEPT = 'image/png,image/jpeg,image/webp,image/svg+xml,image/heic,image/heif,image/avif';

type AspectMode = 'wide' | 'square' | 'freeform';

const ASPECT_RATIOS: Record<AspectMode, number | undefined> = {
  wide: 3,
  square: 1,
  freeform: undefined,
};

interface CurrentLogo {
  fileId: string;
  previewUrl: string;
  sizeBytes: string | null;
  mimeType: string | null;
}

interface UploadResult {
  fileId: string;
  previewUrl: string;
  warnings: string[];
  finalDimensions: { width: number; height: number };
  finalBytes: number;
  originalDimensions: { width: number; height: number };
  originalFormat: string;
}

/**
 * Raw `fetch()` doesn't carry the `X-Port-Id` header that
 * `apiFetch` injects, so the branding endpoints (which require port
 * context) 400 with "No active port". We can't swap to `apiFetch`
 * here because the upload payload is `FormData`, not JSON. So resolve
 * the active port from the URL the same way `apiFetch` does and
 * attach the header manually.
 */
async function brandingHeaders(): Promise<Headers> {
  const headers = new Headers();
  if (typeof window === 'undefined') return headers;
  const slug = window.location.pathname.split('/').filter(Boolean)[0];
  if (!slug || slug === 'login' || slug === 'portal' || slug === 'api' || slug === 'dashboard') {
    return headers;
  }
  const portId = await resolvePortIdFromSlug(slug);
  if (portId) headers.set('X-Port-Id', portId);
  return headers;
}

const WARNING_LABELS: Record<string, string> = {
  trimmed: 'Auto-trimmed whitespace borders',
  resized: 'Downscaled for size',
  'no-alpha': 'No transparent background - will show a white box on dark headers',
  'jpeg-source': 'JPEG source - prefer PNG with alpha for crisp rendering',
  'svg-rasterized': 'SVG rasterized to PNG at 300 DPI',
  'heic-converted': 'HEIC/HEIF converted to PNG',
  'webp-converted': 'WebP converted to PNG',
};

function centeredCrop(width: number, height: number, aspect: number): Crop {
  return centerCrop(makeAspectCrop({ unit: '%', width: 90 }, aspect, width, height), width, height);
}

export function PdfLogoUploader() {
  const { confirm, dialog: confirmDialog } = useConfirmation();
  const [current, setCurrent] = useState<CurrentLogo | null>(null);
  const [loading, setLoading] = useState(true);
  const [working, setWorking] = useState(false);
  const [file, setFile] = useState<File | null>(null);
  const [objectUrl, setObjectUrl] = useState<string | null>(null);
  const [crop, setCrop] = useState<Crop>();
  const [completedCrop, setCompletedCrop] = useState<PixelCrop | null>(null);
  const [aspectMode, setAspectMode] = useState<AspectMode>('wide');
  const [warnings, setWarnings] = useState<string[]>([]);
  const [uploadInfo, setUploadInfo] = useState<UploadResult | null>(null);
  const imgRef = useRef<HTMLImageElement | null>(null);
  const fileInputRef = useRef<HTMLInputElement | null>(null);

  useEffect(() => {
    void refresh();
  }, []);

  useEffect(() => {
    return () => {
      if (objectUrl) URL.revokeObjectURL(objectUrl);
    };
  }, [objectUrl]);

  async function refresh() {
    setLoading(true);
    try {
      const headers = await brandingHeaders();
      const res = await fetch('/api/v1/admin/branding/logo', { headers });
      if (!res.ok) throw new Error(`GET ${res.status}`);
      const body = (await res.json()) as { data: CurrentLogo | null };
      setCurrent(body.data ?? null);
    } catch (err) {
      toastError(err);
    } finally {
      setLoading(false);
    }
  }

  function pick(f: File | null) {
    if (!f) return;
    if (objectUrl) URL.revokeObjectURL(objectUrl);
    const url = URL.createObjectURL(f);
    setFile(f);
    setObjectUrl(url);
    setCrop(undefined);
    setCompletedCrop(null);
    setWarnings([]);
    setUploadInfo(null);
  }

  function onImageLoad(e: React.SyntheticEvent<HTMLImageElement>) {
    const { naturalWidth: w, naturalHeight: h } = e.currentTarget;
    const aspect = ASPECT_RATIOS[aspectMode];
    if (aspect) setCrop(centeredCrop(w, h, aspect));
  }

  function changeAspect(mode: AspectMode) {
    setAspectMode(mode);
    const img = imgRef.current;
    const aspect = ASPECT_RATIOS[mode];
    if (img && aspect) {
      setCrop(centeredCrop(img.naturalWidth, img.naturalHeight, aspect));
    } else {
      setCrop(undefined);
    }
  }

  async function upload() {
    if (!file) return;
    setWorking(true);
    try {
      const fd = new FormData();
      fd.append('file', file);
      if (completedCrop && completedCrop.width > 0 && completedCrop.height > 0) {
        fd.append(
          'crop',
          JSON.stringify({
            x: completedCrop.x,
            y: completedCrop.y,
            width: completedCrop.width,
            height: completedCrop.height,
          }),
        );
      }
      const headers = await brandingHeaders();
      const res = await fetch('/api/v1/admin/branding/logo', {
        method: 'POST',
        headers,
        body: fd,
      });
      const body = (await res.json()) as { data: UploadResult } | { error?: { message?: string } };
      if (!res.ok) {
        const message = ('error' in body && body.error?.message) || `Upload failed (${res.status})`;
        throw new Error(message);
      }
      const data = (body as { data: UploadResult }).data;
      setUploadInfo(data);
      setWarnings(data.warnings ?? []);
      toast.success('Logo saved');
      await refresh();
      // Clear the local crop staging so the preview now shows the stored logo.
      setFile(null);
      if (objectUrl) URL.revokeObjectURL(objectUrl);
      setObjectUrl(null);
    } catch (err) {
      toastError(err);
    } finally {
      setWorking(false);
    }
  }

  async function clear() {
    const ok = await confirm({
      title: 'Remove PDF logo',
      description: 'Remove the PDF logo? Future reports will fall back to the port name.',
      confirmLabel: 'Remove',
    });
    if (!ok) return;
    setWorking(true);
    try {
      const headers = await brandingHeaders();
      const res = await fetch('/api/v1/admin/branding/logo', { method: 'DELETE', headers });
      if (!res.ok && res.status !== 204) throw new Error(`DELETE ${res.status}`);
      toast.success('Logo removed');
      setCurrent(null);
      setUploadInfo(null);
      setWarnings([]);
    } catch (err) {
      toastError(err);
    } finally {
      setWorking(false);
    }
  }

  function openSamplePdf() {
    window.open('/api/v1/admin/branding/logo/sample-pdf', '_blank');
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>PDF logo</CardTitle>
        <CardDescription>
          Used as the header logo on every internal PDF the CRM generates (reports, expense sheets,
          record exports). Separate from the email/UI logo so PDFs can have a higher-resolution,
          alpha-channel version optimized for print.
        </CardDescription>
      </CardHeader>
      <CardContent className="space-y-6">
        <div className="rounded-md bg-muted/40 p-4 text-sm text-muted-foreground space-y-1">
          <p>Use PNG or SVG with a transparent background.</p>
          <p>Minimum 200×200px; recommended 600×200px (wide) or 400×400px (square).</p>
          <p>Max 5MB; we&apos;ll auto-trim, downscale, and convert to PNG.</p>
          <p>Avoid JPEGs unless the background is solid white.</p>
        </div>

        {loading ? (
          <div className="flex items-center gap-2 text-sm text-muted-foreground">
            <Loader2 className="h-4 w-4 animate-spin" aria-hidden /> Loading current logo…
          </div>
        ) : current ? (
          <div className="flex items-start gap-4 rounded-md border p-4">
            <div className="flex h-20 w-44 items-center justify-center rounded bg-[#0f172a] p-2">
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src={current.previewUrl}
                alt="Current PDF logo on dark header"
                className="max-h-full max-w-full object-contain"
              />
            </div>
            <div className="flex-1 space-y-1">
              <div className="text-sm font-medium">Current logo</div>
              <div className="text-xs text-muted-foreground">
                {current.sizeBytes
                  ? `${(Number(current.sizeBytes) / 1024).toFixed(1)} KB`
                  : 'size unknown'}{' '}
                · {current.mimeType ?? 'image'}
              </div>
              <div className="flex gap-2 pt-2">
                <Button variant="outline" size="sm" onClick={openSamplePdf}>
                  Test with sample PDF
                </Button>
                <Button variant="outline" size="sm" onClick={clear} disabled={working}>
                  <Trash2 className="mr-1 h-3 w-3" aria-hidden /> Remove
                </Button>
              </div>
            </div>
          </div>
        ) : (
          <div className="rounded-md border border-dashed p-4 text-sm text-muted-foreground">
            No PDF logo configured yet. Reports use the port name as a text header.
          </div>
        )}

        <div className="space-y-2">
          <Label>Upload a new logo</Label>
          <input
            ref={fileInputRef}
            type="file"
            accept={ACCEPT}
            onChange={(e) => pick(e.target.files?.[0] ?? null)}
            className="block w-full text-sm file:mr-3 file:rounded file:border-0 file:bg-primary file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-primary-foreground"
          />
        </div>

        {objectUrl ? (
          <div className="space-y-4 rounded-md border p-4">
            <div className="flex items-center gap-2 text-sm">
              <span>Crop:</span>
              {(['wide', 'square', 'freeform'] as const).map((m) => (
                <button
                  type="button"
                  key={m}
                  onClick={() => changeAspect(m)}
                  className={`rounded border px-2 py-0.5 text-xs capitalize ${
                    aspectMode === m
                      ? 'border-primary bg-primary text-primary-foreground'
                      : 'border-input hover:bg-accent'
                  }`}
                >
                  {m === 'wide' ? 'Wide 3:1' : m === 'square' ? 'Square 1:1' : 'Freeform'}
                </button>
              ))}
            </div>
            <ReactCrop
              crop={crop}
              onChange={(_c, percent) => setCrop(percent)}
              onComplete={(c) => setCompletedCrop(c)}
              aspect={ASPECT_RATIOS[aspectMode]}
              className="max-h-[400px]"
            >
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                ref={imgRef}
                src={objectUrl}
                alt="Logo to crop"
                onLoad={onImageLoad}
                className="max-h-[400px]"
              />
            </ReactCrop>
            <div className="flex gap-2">
              <Button onClick={upload} disabled={working}>
                {working ? (
                  <Loader2 className="mr-1 h-3 w-3 animate-spin" aria-hidden />
                ) : (
                  <Upload className="mr-1 h-3 w-3" aria-hidden />
                )}
                Save logo
              </Button>
              <Button
                variant="outline"
                onClick={() => {
                  setFile(null);
                  if (objectUrl) URL.revokeObjectURL(objectUrl);
                  setObjectUrl(null);
                  setCrop(undefined);
                  setCompletedCrop(null);
                }}
                disabled={working}
              >
                Cancel
              </Button>
              <Button
                variant="ghost"
                onClick={() => fileInputRef.current?.click()}
                disabled={working}
              >
                <RefreshCw className="mr-1 h-3 w-3" aria-hidden /> Choose a different file
              </Button>
            </div>
          </div>
        ) : null}

        {uploadInfo ? (
          <div className="rounded-md border p-4 text-sm space-y-2">
            <div className="font-medium">Processed</div>
            <div className="text-xs text-muted-foreground">
              From {uploadInfo.originalFormat.toUpperCase()} {uploadInfo.originalDimensions.width}×
              {uploadInfo.originalDimensions.height}
              {' → '}
              PNG {uploadInfo.finalDimensions.width}×{uploadInfo.finalDimensions.height} (
              {(uploadInfo.finalBytes / 1024).toFixed(1)} KB)
            </div>
            {warnings.length > 0 ? (
              <ul className="list-disc space-y-0.5 pl-5 text-xs">
                {warnings.map((w) => (
                  <li key={w}>{WARNING_LABELS[w] ?? w}</li>
                ))}
              </ul>
            ) : null}
          </div>
        ) : null}
        {confirmDialog}
      </CardContent>
    </Card>
  );
}
