/**
 * AttachmentList - reusable file list card
 *
 * Pulls a parent's attachments from the cache, falling back to a
 * fetch if the cache is cold. Renders a list with file-type icon,
 * size, uploader, and download / delete buttons. The server enforces
 * RBAC on delete — we just show the button to everyone for v1.
 *
 * The "Preview" button only appears for types the browser can render
 * inline (PDF + common image formats). Everything else falls back to
 * "Download" so the user can open it in an external viewer.
 */

import { useEffect, useMemo, useState } from 'react';
import {
  FileText, Image as ImageIcon, FileSpreadsheet, Presentation,
  FileArchive, Download, Trash2, Eye, Loader2, AlertCircle, Paperclip,
  Video, Music,
} from 'lucide-react';
import type { Attachment } from '../store/attachments';
import { useAttachmentStore } from '../store/attachments';
import { useAuthStore } from '../store/auth';

interface AttachmentListProps {
  parent_kind: string;
  parent_id: number;
  onPreview?: (att: Attachment) => void;
  emptyMessage?: string;
  refreshKey?: number; // bump to force a refetch
  className?: string;
  captionPrefix?: string; // Filter attachments by caption prefix
}

// Map a mime type to an icon + colour swatch. PDFs go red, images blue,
// office docs follow their conventional office palette. Anything
// unmatched gets a neutral paperclip.
type IconEntry = { Icon: any; bg: string; label: string };

function classify(mime: string): IconEntry {
  if (mime === 'application/pdf') {
    return { Icon: FileText, bg: 'bg-rose-50 text-rose-600 border-rose-100', label: 'PDF' };
  }
  if (mime.startsWith('image/')) {
    return { Icon: ImageIcon, bg: 'bg-blue-50 text-blue-600 border-blue-100', label: 'Image' };
  }
  if (mime.startsWith('video/')) {
    return { Icon: Video, bg: 'bg-indigo-50 text-indigo-600 border-indigo-100', label: 'Video' };
  }
  if (mime.startsWith('audio/')) {
    return { Icon: Music, bg: 'bg-violet-50 text-violet-600 border-violet-100', label: 'Audio' };
  }
  if (mime === 'application/msword' || mime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
    return { Icon: FileText, bg: 'bg-blue-50 text-blue-600 border-blue-100', label: 'Doc' };
  }
  if (mime === 'application/vnd.ms-excel' || mime === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
    return { Icon: FileSpreadsheet, bg: 'bg-emerald-50 text-emerald-600 border-emerald-100', label: 'XLS' };
  }
  if (mime === 'application/vnd.ms-powerpoint' || mime === 'application/vnd.openxmlformats-officedocument.presentationml.presentation') {
    return { Icon: Presentation, bg: 'bg-amber-50 text-amber-600 border-amber-100', label: 'PPT' };
  }
  if (mime === 'application/zip' || mime === 'application/x-zip-compressed') {
    return { Icon: FileArchive, bg: 'bg-slate-100 text-slate-600 border-slate-200', label: 'ZIP' };
  }
  if (mime === 'text/plain') {
    return { Icon: FileText, bg: 'bg-slate-100 text-slate-600 border-slate-200', label: 'TXT' };
  }
  return { Icon: Paperclip, bg: 'bg-slate-100 text-slate-600 border-slate-200', label: 'File' };
}

function formatSize(bytes: number): string {
  if (!Number.isFinite(bytes) || bytes < 0) return '—';
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

function formatDate(iso: string | undefined): string {
  if (!iso) return '';
  try {
    const d = new Date(iso);
    return d.toLocaleDateString(undefined, { day: '2-digit', month: 'short', year: 'numeric' });
  } catch {
    return iso;
  }
}

const PREVIEWABLE_PREFIXES = ['image/', 'application/pdf', 'video/', 'audio/'];

export function AttachmentList({
  parent_kind,
  parent_id,
  onPreview,
  emptyMessage = 'No attachments yet',
  refreshKey = 0,
  className = '',
  captionPrefix,
}: AttachmentListProps) {
  const { byParent, loading, error, list, downloadUrl, remove, clearError } = useAttachmentStore();
  const user = useAuthStore(s => s.user);
  const [deletingUid, setDeletingUid] = useState<string | null>(null);

  const items = useMemo<Attachment[]>(
    () => {
      const rows = byParent[`${parent_kind}:${parent_id}`] || [];
      if (captionPrefix) {
        return rows.filter(r => r.caption && r.caption.startsWith(captionPrefix));
      }
      return rows;
    },
    [byParent, parent_kind, parent_id, captionPrefix]
  );

  useEffect(() => {
    list(parent_kind, parent_id).catch(() => {
      // store.list already sets error state
    });
  }, [parent_kind, parent_id, refreshKey, list]);

  const handleDelete = async (att: Attachment) => {
    if (deletingUid) return;
    setDeletingUid(att.uid);
    try {
      await remove(att.uid);
    } catch {
      // store surfaces the error
    } finally {
      setDeletingUid(null);
    }
  };

  const handleDownload = (att: Attachment) => {
    const url = downloadUrl(att.stored_filename);
    const a = document.createElement('a');
    a.href = url;
    a.download = att.original_filename;
    a.rel = 'noopener';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };

  const handlePreview = (att: Attachment) => {
    if (onPreview) {
      onPreview(att);
    } else {
      // Default: trigger the browser-native open path (which the
      // iframe/img modal would do). Fall back to download if the
      // browser can't render it inline.
      const url = downloadUrl(att.stored_filename);
      window.open(url, '_blank', 'noopener');
    }
  };

  return (
    <div className={`bg-card border border-border rounded-2xl ${className}`}>
      {error && (
        <div className="flex items-start gap-2 p-3 m-3 text-sm rounded-xl bg-rose-50 border border-rose-200 text-rose-700">
          <AlertCircle size={16} className="mt-0.5 shrink-0" />
          <div className="flex-1">{error}</div>
          <button
            type="button"
            onClick={clearError}
            className="text-rose-500 hover:text-rose-700 text-xs font-bold uppercase tracking-widest"
          >
            Dismiss
          </button>
        </div>
      )}

      {loading && items.length === 0 ? (
        <div className="flex items-center gap-2 p-6 text-text-muted text-sm">
          <Loader2 size={16} className="animate-spin" />
          Loading attachments…
        </div>
      ) : items.length === 0 ? (
        <div className="p-6 text-sm text-text-muted text-center">{emptyMessage}</div>
      ) : (
        <ul className="divide-y divide-border">
          {items.map(att => {
            const { Icon, bg, label } = classify(att.mime_type);
            const canPreview = PREVIEWABLE_PREFIXES.some(p => att.mime_type.startsWith(p));
            const isDeleting = deletingUid === att.uid;

            return (
              <li
                key={att.uid}
                className="flex items-center gap-3 p-3 sm:p-4 hover:bg-muted/40 transition-colors"
              >
                <div className={`shrink-0 w-10 h-10 rounded-xl border flex items-center justify-center ${bg}`}>
                  <Icon size={18} aria-hidden="true" />
                  <span className="sr-only">{label}</span>
                </div>

                <div className="min-w-0 flex-1">
                  <div className="flex items-baseline gap-2 flex-wrap">
                    <span className="font-semibold text-sm text-text truncate" title={att.original_filename}>
                      {att.original_filename}
                    </span>
                    {att.caption && (
                      <span className="text-[0.65rem] uppercase tracking-widest font-bold text-text-muted">
                        {att.caption}
                      </span>
                    )}
                  </div>
                  <div className="flex items-center gap-2 text-[0.7rem] text-text-muted font-medium mt-0.5">
                    <span>{formatSize(att.size_bytes)}</span>
                    <span aria-hidden="true">·</span>
                    <span>{formatDate(att.uploaded_at)}</span>
                    {att.uploaded_by && user && att.uploaded_by !== user.id && (
                      <>
                        <span aria-hidden="true">·</span>
                        <span className="truncate">by #{att.uploaded_by}</span>
                      </>
                    )}
                  </div>
                </div>

                <div className="flex items-center gap-1 shrink-0">
                  {canPreview && (
                    <button
                      type="button"
                      onClick={() => handlePreview(att)}
                      className="p-2 rounded-xl text-text-muted hover:text-primary hover:bg-blue-50/50 transition-colors"
                      title="Preview"
                      aria-label={`Preview ${att.original_filename}`}
                    >
                      <Eye size={16} />
                    </button>
                  )}
                  <button
                    type="button"
                    onClick={() => handleDownload(att)}
                    className="p-2 rounded-xl text-text-muted hover:text-primary hover:bg-blue-50/50 transition-colors"
                    title="Download"
                    aria-label={`Download ${att.original_filename}`}
                  >
                    <Download size={16} />
                  </button>
                  <button
                    type="button"
                    onClick={() => handleDelete(att)}
                    disabled={isDeleting}
                    className="p-2 rounded-xl text-text-muted hover:text-rose-600 hover:bg-rose-50 transition-colors disabled:opacity-50"
                    title="Delete"
                    aria-label={`Delete ${att.original_filename}`}
                  >
                    {isDeleting ? <Loader2 size={16} className="animate-spin" /> : <Trash2 size={16} />}
                  </button>
                </div>
              </li>
            );
          })}
        </ul>
      )}
    </div>
  );
}

export default AttachmentList;
