'use client';

import { useState, useEffect, useRef } from 'react';
import { Card, Portal, MediaItem, CardType } from '@/types';
import { EXTERNAL_LINK_ENABLED as EXTERNAL_LINK_DEFAULT } from '@/lib/config';

function StyledSelect<T extends string>({
  value,
  onChange,
  options,
}: {
  value: T;
  onChange: (v: T) => void;
  options: { value: T; label: string; style?: React.CSSProperties }[];
}) {
  const [open, setOpen] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;
    const onClick = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
    };
    const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onClick);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onClick);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  const current = options.find(o => o.value === value);
  // Fallback: se il value non matcha nessuna opzione (es. tipo disattivato dalla flag), mostra il valore raw prettificato
  const displayLabel = current?.label
    ?? (typeof value === 'string' && value.length > 0
        ? value.replace(/_/g, ' ').toLowerCase().replace(/\b\w/g, c => c.toUpperCase())
        : '');
  const inputBase = "w-full border border-gray-300 p-2.5 rounded-lg outline-none focus:ring-2 focus:ring-blue-500 bg-white text-gray-900";

  return (
    <div ref={ref} className="relative">
      <button
        type="button"
        onClick={() => setOpen(o => !o)}
        className={`${inputBase} text-left flex items-center justify-between cursor-pointer`}
      >
        <span className={displayLabel ? '' : 'text-gray-400'} style={current?.style}>{displayLabel || 'Seleziona…'}</span>
        <span className={`text-gray-500 transition-transform ${open ? 'rotate-180' : ''}`}>▾</span>
      </button>
      {open && (
        <div className="absolute left-0 right-0 top-full mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-30 overflow-hidden">
          {options.map(o => (
            <button
              key={o.value}
              type="button"
              onClick={() => { onChange(o.value); setOpen(false); }}
              className={`w-full text-left px-3 py-2.5 hover:bg-blue-50 transition-colors ${o.value === value ? 'bg-blue-100 font-semibold text-blue-700' : 'text-gray-800'}`}
              style={o.style}
            >
              {o.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

const VIDEO_EXTENSIONS = 'mp4|m4v|webm|mov|qt|mkv|avi|divx|wmv|asf|flv|f4v|3gp|3gpp|3g2|mts|m2ts|ts|mpg|mpeg|vob|mxf|ogv|ogg';
// Sottoinsieme di formati video davvero riproducibili dai browser moderni
const PLAYBACK_SUPPORTED_VIDEO = 'mp4|m4v|webm|mov|qt|ogv|ogg';
const PLAYBACK_SUPPORTED_LABEL = 'MP4, M4V, WebM, MOV, OGV';

const isVideoUrl = (url: string) => new RegExp(`\\.(${VIDEO_EXTENSIONS})(\\?|$)`, 'i').test(url);
const isPdfFile = (file: File) =>
  file.type === 'application/pdf' || /\.pdf$/i.test(file.name);
const isVideoFile = (file: File) =>
  file.type.startsWith('video/') || new RegExp(`\\.(${VIDEO_EXTENSIONS})$`, 'i').test(file.name);
const isPlayableVideoFile = (file: File) =>
  new RegExp(`\\.(${PLAYBACK_SUPPORTED_VIDEO})$`, 'i').test(file.name);

const previewFontFamily = (filename: string): string =>
  `PortalPreview-${filename.replace(/[^A-Za-z0-9]/g, '_')}`;

const fontFormatFromName = (filename: string): string => {
  const ext = filename.match(/\.([^.]+)$/)?.[1].toLowerCase() ?? 'woff2';
  return ({ woff2: 'woff2', woff: 'woff', ttf: 'truetype', otf: 'opentype' } as Record<string, string>)[ext] ?? 'woff2';
};

const extractFileName = (url: string): string => {
  const match = url.match(/[?&]name=([^&]+)/);
  if (match) return decodeURIComponent(match[1]);
  const seg = url.split('/').pop() || 'download';
  return seg.split('?')[0];
};

async function uploadBlobAsImage(blob: Blob, name: string): Promise<string | null> {
  const formData = new FormData();
  formData.append('file', new File([blob], name, { type: blob.type || 'image/png' }));
  const res = await fetch('/api/upload', { method: 'POST', body: formData });
  const data = await res.json();
  return data.url || null;
}

async function extractVideoFrame(file: File): Promise<Blob | null> {
  const url = URL.createObjectURL(file);
  try {
    const video = document.createElement('video');
    video.muted = true;
    video.playsInline = true;
    video.preload = 'metadata';
    video.src = url;

    await new Promise<void>((resolve, reject) => {
      video.addEventListener('loadedmetadata', () => resolve(), { once: true });
      video.addEventListener('error', () => reject(new Error('video load error')), { once: true });
    });

    // Seek slightly past 0 — at exactly 0 some codecs return a black frame
    video.currentTime = Math.min(0.1, Math.max(0, video.duration / 10));
    await new Promise<void>((resolve, reject) => {
      video.addEventListener('seeked', () => resolve(), { once: true });
      video.addEventListener('error', () => reject(new Error('video seek error')), { once: true });
    });

    const canvas = document.createElement('canvas');
    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;
    const ctx = canvas.getContext('2d');
    if (!ctx) return null;
    ctx.drawImage(video, 0, 0);

    return await new Promise<Blob | null>((resolve) =>
      canvas.toBlob((b) => resolve(b), 'image/jpeg', 0.85)
    );
  } finally {
    URL.revokeObjectURL(url);
  }
}

async function pdfToImageItems(
  file: File,
  onProgress: (page: number, total: number) => void
): Promise<MediaItem[]> {
  const pdfjs = await import('pdfjs-dist');
  // Worker file is copied to /public via the postinstall script
  pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.mjs';

  const arrayBuffer = await file.arrayBuffer();
  const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
  const baseName = file.name.replace(/\.pdf$/i, '').replace(/[^a-zA-Z0-9-_]/g, '_');
  const items: MediaItem[] = [];

  for (let i = 1; i <= pdf.numPages; i++) {
    onProgress(i, pdf.numPages);
    const page = await pdf.getPage(i);
    const viewport = page.getViewport({ scale: 1.5 });
    const canvas = document.createElement('canvas');
    canvas.width = viewport.width;
    canvas.height = viewport.height;
    const ctx = canvas.getContext('2d');
    if (!ctx) continue;
    await page.render({ canvasContext: ctx, viewport }).promise;

    const blob: Blob = await new Promise((resolve, reject) => {
      canvas.toBlob(b => b ? resolve(b) : reject(new Error('toBlob failed')), 'image/png');
    });

    const url = await uploadBlobAsImage(blob, `${baseName}-page${i}.png`);
    if (url) items.push({ url });
  }

  return items;
}

export default function AdminDashboard() {
  const [activeTab, setActiveTab] = useState<'cards' | 'settings'>('cards');

  // Card State
  const [cards, setCards] = useState<Card[]>([]);
  const [isEditing, setIsEditing] = useState<Partial<Card> | null>(null);

  // Portal State
  const [portal, setPortal] = useState<Partial<Portal>>({});
  const [savingPortal, setSavingPortal] = useState(false);
  const [uploading, setUploading] = useState<{ [key: string]: boolean }>({});

  // NEW UI STATES: Toast and Confirm Dialog
  const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null);
  const [confirmDialog, setConfirmDialog] = useState<{ message: string, onConfirm: () => void } | null>(null);
  const [pdfProgress, setPdfProgress] = useState<{ name: string; page: number; total: number } | null>(null);
  const [availableFonts, setAvailableFonts] = useState<string[]>([]);

  // External Link feature flag: priorità al setting del portale, fallback alla costante in lib/config.
  const externalLinksOn = portal.externalLinkEnabled ?? EXTERNAL_LINK_DEFAULT;

  // Helper to show auto-dismissing toast
  const showToast = (message: string, type: 'success' | 'error' = 'success') => {
    setToast({ message, type });
    setTimeout(() => setToast(null), type === 'error' ? 6000 : 3000);
  };

  useEffect(() => {
    fetch('/api/cards').then(res => res.json()).then(setCards);
    fetch('/api/portals').then(res => res.json()).then(data => data && setPortal(data));
    fetch('/api/fonts').then(res => res.json()).then(setAvailableFonts).catch(() => setAvailableFonts([]));
  }, []);

  const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>, field: string, isPortal = false) => {
    if (!e.target.files?.[0]) return;
    setUploading(prev => ({ ...prev, [field]: true }));

    const formData = new FormData();
    formData.append('file', e.target.files[0]);
    const res = await fetch('/api/upload', { method: 'POST', body: formData });
    const data = await res.json();

    if (data.url) {
      if (isPortal) {
        setPortal(prev => ({ ...prev, [field]: data.url }));
      } else {
        setIsEditing(prev => ({ ...prev, [field]: data.url }));
      }
    }
    setUploading(prev => ({ ...prev, [field]: false }));
  };

  const handleUploadExtraMedia = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files || files.length === 0) return;
    setUploading(prev => ({ ...prev, extraMedia: true }));

    const startedWithoutCover = !isEditing?.imageUrl;
    let pendingCover: string | null = null;
    const canPromote = () => startedWithoutCover && !pendingCover;

    // Pre-filtro: scarta video con formati non riproducibili nei browser
    const rejected: string[] = [];
    const acceptedFiles: File[] = [];
    for (const file of Array.from(files)) {
      if (isVideoFile(file) && !isPlayableVideoFile(file)) {
        rejected.push(file.name);
      } else {
        acceptedFiles.push(file);
      }
    }
    if (rejected.length > 0) {
      const list = rejected.length <= 3
        ? rejected.join(', ')
        : `${rejected.slice(0, 3).join(', ')} e altri ${rejected.length - 3}`;
      showToast(
        `Formato non supportato! I formati supportati sono: ${PLAYBACK_SUPPORTED_LABEL}. File ignorati: ${list}`,
        'error'
      );
    }
    if (acceptedFiles.length === 0) {
      setUploading(prev => ({ ...prev, extraMedia: false }));
      e.target.value = '';
      return;
    }

    const uploaded: MediaItem[] = [];
    for (const file of acceptedFiles) {
      try {
        if (isPdfFile(file)) {
          const items = await pdfToImageItems(file, (page, total) =>
            setPdfProgress({ name: file.name, page, total })
          );
          setPdfProgress(null);
          if (items.length > 0 && canPromote()) {
            // Promote the first PDF page to cover; skip it from the gallery to avoid duplication.
            pendingCover = items[0].url;
            uploaded.push(...items.slice(1));
          } else {
            uploaded.push(...items);
          }
        } else {
          const formData = new FormData();
          formData.append('file', file);
          const res = await fetch('/api/upload', { method: 'POST', body: formData });
          const data = await res.json();
          if (!data.url) continue;

          if (isVideoFile(file)) {
            // Video always goes to the gallery so users can play it.
            uploaded.push({ url: data.url });
            // If no cover yet, extract the first frame and use it as the cover.
            if (canPromote()) {
              try {
                const blob = await extractVideoFrame(file);
                if (blob) {
                  const baseName = file.name.replace(/\.[^.]+$/, '').replace(/[^a-zA-Z0-9-_]/g, '_');
                  const posterUrl = await uploadBlobAsImage(blob, `${baseName}-poster.jpg`);
                  if (posterUrl) pendingCover = posterUrl;
                }
              } catch (err) {
                console.warn('Could not extract video poster for', file.name, err);
              }
            }
          } else {
            // Plain image
            if (canPromote()) {
              // Promote to cover; skip the gallery to avoid duplication.
              pendingCover = data.url;
            } else {
              uploaded.push({ url: data.url });
            }
          }
        }
      } catch (err) {
        console.error('Upload failed for', file.name, err);
        showToast(`Failed to process "${file.name}".`);
        setPdfProgress(null);
      }
    }

    setIsEditing(prev => ({
      ...prev,
      imageUrl: (startedWithoutCover && pendingCover) ? pendingCover : (prev?.imageUrl || ''),
      extraMedia: [...(prev?.extraMedia || []), ...uploaded],
    }));
    setUploading(prev => ({ ...prev, extraMedia: false }));
    e.target.value = '';
  };

  const removeExtraMedia = (index: number) => {
    setIsEditing(prev => ({
      ...prev,
      extraMedia: (prev?.extraMedia || []).filter((_, i) => i !== index),
    }));
  };

  const moveExtraMedia = (index: number, direction: 'up' | 'down') => {
    setIsEditing(prev => {
      const items = [...(prev?.extraMedia || [])];
      if (direction === 'up' && index > 0) {
        [items[index - 1], items[index]] = [items[index], items[index - 1]];
      } else if (direction === 'down' && index < items.length - 1) {
        [items[index + 1], items[index]] = [items[index], items[index + 1]];
      } else {
        return prev;
      }
      return { ...prev, extraMedia: items };
    });
  };

  const toggleAutoplay = (index: number) => {
    setIsEditing(prev => ({
      ...prev,
      extraMedia: (prev?.extraMedia || []).map((m, i) =>
        i === index ? { ...m, autoplay: !m.autoplay } : m
      ),
    }));
  };

  const toggleMuted = (index: number) => {
    setIsEditing(prev => ({
      ...prev,
      extraMedia: (prev?.extraMedia || []).map((m, i) =>
        i === index ? { ...m, muted: !m.muted } : m
      ),
    }));
  };

  const handleSaveCard = async () => {
    if (!isEditing) return;
    const generateSafeId = () => 'card-' + Date.now().toString(36) + Math.random().toString(36).substring(2);
    const newCard = { ...isEditing, id: isEditing.id || generateSafeId() } as Card;
    
    await fetch('/api/cards', { 
      method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newCard)
    });

    setCards(prev => {
      const exists = prev.find(c => c.id === newCard.id);
      return exists ? prev.map(c => c.id === newCard.id ? newCard : c) : [...prev, newCard];
    });
    setIsEditing(null);
  };

  const handleDeleteCard = (id: string) => {
    // Replace window.confirm with our custom dialog
    setConfirmDialog({
      message: 'Are you sure you want to delete this card? This action cannot be undone.',
      onConfirm: async () => {
        await fetch(`/api/cards?id=${id}`, { method: 'DELETE' });
        setCards(prev => prev.filter(c => c.id !== id));
        setConfirmDialog(null);
        showToast('Card successfully deleted.');
      }
    });
  };

  const moveCard = async (index: number, direction: 'up' | 'down') => {
    const newCards = [...cards];
    
    if (direction === 'up' && index > 0) {
      [newCards[index - 1], newCards[index]] = [newCards[index], newCards[index - 1]];
    } else if (direction === 'down' && index < newCards.length - 1) {
      [newCards[index + 1], newCards[index]] = [newCards[index], newCards[index + 1]];
    } else {
      return; // Do nothing if trying to move out of bounds
    }
    
    // Recalculate displayOrder for the whole array
    const updatedCards = newCards.map((c, i) => ({ ...c, displayOrder: i }));
    
    // Optimistically update the UI
    setCards(updatedCards);
    
    // Persist the new order to the backend
    await fetch('/api/cards', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(updatedCards)
    });
  };

  const handleSavePortal = async () => {
    setSavingPortal(true);
    await fetch('/api/portals', {
      method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(portal)
    });
    setSavingPortal(false);
    showToast('Portal settings saved successfully!'); // Replaced window.alert
  };

  // Shared Input Classes for high contrast
  const inputClasses = "w-full border border-gray-300 p-2.5 rounded-lg outline-none focus:ring-2 focus:ring-blue-500 bg-white text-gray-900 placeholder-gray-400";

  return (
    <div className="min-h-screen bg-gray-50 font-sans pb-12">
      {/* Top Header */}
      <div className="bg-blue-900 text-white shadow-md py-6 px-4">
        <div className="max-w-5xl mx-auto flex justify-between items-center">
          <div>
            <h1 className="text-2xl font-bold">Captive Portal CMS</h1>
            <p className="text-sm text-blue-200">Local Administration</p>
          </div>
          <a href="/" target="_blank" className="bg-blue-800 hover:bg-blue-700 text-white px-4 py-2 rounded text-sm transition-colors">
            View Live Portal ↗
          </a>
        </div>
      </div>

      <div className="max-w-5xl mx-auto mt-8 px-4">
        {/* Tab Navigation */}
        <div className="flex space-x-2 mb-6">
          <button onClick={() => setActiveTab('cards')} className={`px-6 py-3 rounded-t-lg font-bold transition-colors ${activeTab === 'cards' ? 'bg-white text-blue-700 border-t-4 border-blue-600 shadow-sm' : 'bg-gray-200 text-gray-600 hover:bg-gray-300'}`}>
            Manage Cards
          </button>
          <button onClick={() => setActiveTab('settings')} className={`px-6 py-3 rounded-t-lg font-bold transition-colors ${activeTab === 'settings' ? 'bg-white text-blue-700 border-t-4 border-blue-600 shadow-sm' : 'bg-gray-200 text-gray-600 hover:bg-gray-300'}`}>
            Portal Settings
          </button>
        </div>

        <div className="bg-white rounded-b-xl rounded-tr-xl shadow-sm border border-gray-200 overflow-hidden min-h-[500px]">
          
          {/* TAB: CARDS */}
          {activeTab === 'cards' && (
            <div className="p-6 md:p-8">
              <div className="flex justify-between items-center mb-8 border-b pb-4">
                <h2 className="text-xl font-bold text-gray-800">Card Grid</h2>
                <button onClick={() => setIsEditing({ title: '', cardType: 'INFO_PAGE', displayOrder: cards.length })} className="bg-blue-600 text-white px-5 py-2.5 rounded-lg shadow-sm hover:bg-blue-700 font-medium">
                  + Add New Card
                </button>
              </div>

              <div className="space-y-3 mb-8">
                {cards.length === 0 && <p className="text-gray-500 italic text-center py-8">No cards available. Create one to get started.</p>}
                {cards.map((card, idx) => (
                  // CHANGED: flex-col on mobile, flex-row on sm+, added gap-4 for mobile spacing
                  <div key={card.id} className="flex flex-col sm:flex-row sm:items-center justify-between p-4 border rounded-lg bg-gray-50 hover:bg-gray-100 transition-colors gap-4">
                    <div className="flex items-center gap-4">
                      {(() => {
                        const previewUrl = card.imageUrl || card.extraMedia?.[0]?.url || '';
                        if (!previewUrl) {
                          return <div className="w-16 h-16 bg-gray-200 rounded-md shadow-sm flex items-center justify-center text-gray-400 text-xs shrink-0">No Image</div>;
                        }
                        return isVideoUrl(previewUrl)
                          ? <video src={previewUrl} className="w-16 h-16 object-cover rounded-md shadow-sm shrink-0" muted playsInline preload="metadata" />
                          : <img src={previewUrl} className="w-16 h-16 object-cover rounded-md shadow-sm shrink-0" alt="" />;
                      })()}
                      <div>
                        <span className="font-semibold text-gray-800 block">{card.title}</span>
                        <span className="text-xs text-gray-500 uppercase tracking-wider">
                          {card.cardType}
                          {card.extraMedia && card.extraMedia.length > 0 && (
                            <span className="text-gray-400 normal-case tracking-normal ml-2">[{card.extraMedia.length}]</span>
                          )}
                        </span>
                      </div>
                    </div>
                    
                    {/* CHANGED: flex-wrap to ensure buttons don't overflow on small screens, w-full on mobile */}
                    <div className="flex flex-wrap items-center gap-2 w-full sm:w-auto justify-end">
                      <button onClick={() => moveCard(idx, 'up')} className="p-2 text-gray-500 hover:text-gray-800 hover:bg-gray-200 rounded" title="Move Up">↑</button>
                      <button onClick={() => moveCard(idx, 'down')} className="p-2 text-gray-500 hover:text-gray-800 hover:bg-gray-200 rounded" title="Move Down">↓</button>
                      <div className="w-px h-6 bg-gray-300 mx-1 hidden sm:block"></div>
                      <button onClick={() => setIsEditing(card)} className="px-4 py-2 text-blue-600 hover:bg-blue-50 rounded font-medium">Edit</button>
                      <button onClick={() => handleDeleteCard(card.id)} className="px-4 py-2 text-red-600 hover:bg-red-50 rounded font-medium">Delete</button>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* TAB: SETTINGS */}
          {activeTab === 'settings' && (
            <div className="p-6 md:p-8">
              <h2 className="text-xl font-bold text-gray-800 mb-8 border-b pb-4">Global Portal Settings</h2>
              
              <div className="grid grid-cols-1 md:grid-cols-2 gap-10">
                <div className="space-y-6">
                  <div>
                    <label className="block text-sm font-semibold text-gray-700 mb-1">Portal Title</label>
                    <input type="text" value={portal.title || ''} onChange={e => setPortal({...portal, title: e.target.value})} className={inputClasses} />
                  </div>
                  
                  <div>
                    <label className="block text-sm font-semibold text-gray-700 mb-1">Welcome Text</label>
                    <textarea value={portal.welcomeText || ''} onChange={e => setPortal({...portal, welcomeText: e.target.value})} className={`${inputClasses} h-32 resize-none`} />
                  </div>

                  <div className="flex gap-8">
                    <div>
                      <label className="block text-sm font-semibold text-gray-700 mb-1">Theme Color</label>
                      <div className="flex items-center gap-4">
                        <input type="color" value={portal.themeColor || '#1e3a8a'} onChange={e => setPortal({...portal, themeColor: e.target.value})} className="h-12 w-12 rounded cursor-pointer border-0 p-0" />
                        <span className="text-gray-900 font-mono font-medium">{portal.themeColor || '#1e3a8a'}</span>
                      </div>
                    </div>
                    
                    {/* NEW: Max Columns Setting updated for 3 */}
                    <div className="flex-1">
                      <label className="block text-sm font-semibold text-gray-700 mb-1">Grid Max Columns: {portal.maxGridColumns || 5}</label>
                      <input 
                        type="range" 
                        min="3" 
                        max="8" 
                        value={portal.maxGridColumns || 5} 
                        onChange={e => setPortal({...portal, maxGridColumns: parseInt(e.target.value)})}
                        className="w-full mt-3 accent-blue-600"
                      />
                      <div className="flex justify-between text-xs text-gray-400 mt-1">
                        <span>3</span><span>4</span><span>5</span><span>6</span><span>7</span><span>8</span>
                      </div>
                    </div>
                  </div>

                  <div>
                    <label className="block text-sm font-semibold text-gray-700 mb-1">Font del portale</label>
                    <style dangerouslySetInnerHTML={{ __html: availableFonts.map(f => `
@font-face {
  font-family: '${previewFontFamily(f)}';
  src: url('/api/fonts?name=${encodeURIComponent(f)}') format('${fontFormatFromName(f)}');
  font-display: swap;
}`).join('') }} />
                    <StyledSelect<string>
                      value={portal.fontFamily ?? ''}
                      onChange={(v) => setPortal({ ...portal, fontFamily: v })}
                      options={[
                        { value: '', label: 'Sistema (Arial)' },
                        ...availableFonts.map(f => ({
                          value: f,
                          label: f.replace(/\.(woff2?|ttf|otf)$/i, ''),
                          style: { fontFamily: `'${previewFontFamily(f)}', Arial, Helvetica, sans-serif` },
                        })),
                      ]}
                    />
                  </div>
                </div>

                <div className="space-y-6">
                  {/* Logo Upload with Remove Button */}
                  <div>
                    <label className="block text-sm font-semibold text-gray-700 mb-1">Logo Image</label>
                    <input type="file" accept="image/*" onChange={e => handleUpload(e, 'logoUrl', true)} className="block w-full text-sm text-gray-900 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:bg-gray-100 cursor-pointer" />
                    {uploading['logoUrl'] && <span className="text-xs text-blue-500">Uploading...</span>}
                    {portal.logoUrl && (
                      <div className="mt-2 bg-gray-100 p-4 rounded inline-block relative border">
                        <img src={portal.logoUrl} className="h-16 object-contain" alt="Logo Preview" />
                        <a href={portal.logoUrl} download={extractFileName(portal.logoUrl)} className="absolute -top-2 right-6 bg-gray-700 hover:bg-gray-800 text-white w-6 h-6 rounded-full text-xs font-bold shadow flex items-center justify-center" title="Scarica" aria-label="Scarica logo">⬇</a>
                        <button onClick={() => setPortal({...portal, logoUrl: ''})} className="absolute -top-2 -right-2 bg-red-500 text-white w-6 h-6 rounded-full text-xs font-bold hover:bg-red-600 shadow">✕</button>
                      </div>
                    )}
                  </div>

                  {/* Hero Upload with Remove Button */}
                  <div>
                    <label className="block text-sm font-semibold text-gray-700 mb-1">Hero Background Image</label>
                    <input type="file" accept="image/*" onChange={e => handleUpload(e, 'heroImageUrl', true)} className="block w-full text-sm text-gray-900 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:bg-gray-100 cursor-pointer" />
                    {uploading['heroImageUrl'] && <span className="text-xs text-blue-500">Uploading...</span>}
                    {portal.heroImageUrl && (
                      <div className="mt-2 relative rounded shadow border inline-block w-full">
                        <img src={portal.heroImageUrl} className="h-32 w-full object-cover rounded" alt="Hero Preview" />
                        <a href={portal.heroImageUrl} download={extractFileName(portal.heroImageUrl)} className="absolute top-2 right-12 bg-gray-700 hover:bg-gray-800 text-white w-8 h-8 flex items-center justify-center rounded-full text-sm font-bold shadow-lg" title="Scarica" aria-label="Scarica hero">⬇</a>
                        <button onClick={() => setPortal({...portal, heroImageUrl: ''})} className="absolute top-2 right-2 bg-red-500 text-white w-8 h-8 flex items-center justify-center rounded-full text-sm font-bold hover:bg-red-600 shadow-lg">✕</button>
                      </div>
                    )}
                  </div>

                  <div className="bg-gray-50 p-4 rounded-lg border border-gray-200 space-y-3">
                     <label className="flex items-center gap-3 cursor-pointer">
                        <input type="checkbox" checked={!!portal.fadeHeroImage} onChange={e => setPortal({...portal, fadeHeroImage: e.target.checked})} className="w-5 h-5 text-blue-600 rounded" />
                        <div>
                          <span className="block text-sm font-semibold text-gray-900">Fade Image into Background Color</span>
                          <span className="block text-xs text-gray-600">Creates a smooth gradient from the top of the image into the solid theme color at the bottom.</span>
                        </div>
                     </label>
                     <label className="flex items-center gap-3 cursor-pointer">
                        <input
                          type="checkbox"
                          checked={portal.externalLinkEnabled ?? EXTERNAL_LINK_DEFAULT}
                          onChange={e => setPortal({...portal, externalLinkEnabled: e.target.checked})}
                          className="w-5 h-5 text-blue-600 rounded"
                        />
                        <div>
                          <span className="block text-sm font-semibold text-gray-900">Abilita &ldquo;External Link&rdquo;</span>
                          <span className="block text-xs text-gray-600">Mostra il tipo &ldquo;External Link&rdquo; nel dropdown del Card Type. Le card esistenti di quel tipo restano comunque visibili e cliccabili.</span>
                        </div>
                     </label>
                  </div>
                </div>
              </div>

              <div className="mt-10 pt-6 border-t border-gray-200 flex justify-end">
                <button onClick={handleSavePortal} disabled={savingPortal} className="bg-blue-600 text-white px-10 py-3 rounded-lg hover:bg-blue-700 font-bold shadow disabled:opacity-50 transition-colors">
                  {savingPortal ? 'Saving...' : 'Save Portal Settings'}
                </button>
              </div>
            </div>
          )}
		  
		  </div> 
      </div> 

      {/* MODAL FOR EDITING/CREATING CARDS */}
      {isEditing && (
        <div 
          className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm p-4 transition-opacity"
          onClick={() => setIsEditing(null)} // Click outside to close
        >
          <div 
            className="bg-white rounded-2xl w-full max-w-3xl max-h-[90vh] overflow-y-auto shadow-2xl p-6 md:p-8 relative animate-in fade-in zoom-in-95 duration-200"
            onClick={(e) => e.stopPropagation()} // Prevent inside clicks from closing
          >
            <button 
              onClick={() => setIsEditing(null)}
              className="absolute top-6 right-6 text-gray-400 hover:text-gray-900 bg-gray-100 hover:bg-gray-200 rounded-full w-8 h-8 flex items-center justify-center transition-colors"
            >
              ✕
            </button>

            <h3 className="text-2xl font-bold mb-6 text-gray-900 border-b pb-4">
              {isEditing.id ? 'Edit Card' : 'Create New Card'}
            </h3>
            
            <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
              <div className="space-y-5">
                <div>
                  <label className="block text-sm font-semibold text-gray-800 mb-1">Title</label>
                  <input type="text" value={isEditing.title || ''} onChange={e => setIsEditing({...isEditing, title: e.target.value})} className={inputClasses} placeholder="e.g., Local History" />
                </div>
                <div>
                  <label className="block text-sm font-semibold text-gray-800 mb-1">Card Type</label>
                  <StyledSelect<CardType>
                    value={(isEditing.cardType || 'INFO_PAGE') as CardType}
                    onChange={(v) => setIsEditing({ ...isEditing, cardType: v })}
                    options={[
                      { value: 'INFO_PAGE', label: 'Info Page' },
                      { value: 'IMAGE_GALLERY', label: 'Image Gallery' },
                      { value: 'BOOK', label: 'Flip-Book' },
                      ...(externalLinksOn ? [{ value: 'EXTERNAL_LINK' as CardType, label: 'External Link' }] : []),
                    ]}
                  />
                </div>
                {isEditing.cardType === 'EXTERNAL_LINK' ? (
                  <>
                    <div>
                      <label className="block text-sm font-semibold text-gray-800 mb-1">URL</label>
                      <input
                        type="url"
                        value={isEditing.actionUrl || ''}
                        onChange={e => setIsEditing({ ...isEditing, actionUrl: e.target.value })}
                        className={inputClasses}
                        placeholder="https://esempio.it/pagina"
                      />
                    </div>
                    <div>
                      <label className="block text-sm font-semibold text-gray-800 mb-1">Testo del link</label>
                      <input
                        type="text"
                        value={isEditing.shortDescription || ''}
                        onChange={e => setIsEditing({ ...isEditing, shortDescription: e.target.value })}
                        className={inputClasses}
                        placeholder="es. Visita il sito ufficiale"
                      />
                      <p className="text-xs text-gray-500 mt-1">Testo visualizzato come link cliccabile nel modale. Se vuoto, viene mostrata l&rsquo;URL stessa.</p>
                    </div>
                    <div className="bg-gray-50 p-3 rounded-lg border border-gray-200">
                      <label className="flex items-start gap-3 cursor-pointer">
                        <input
                          type="checkbox"
                          checked={!!isEditing.redirectOnClick}
                          onChange={e => setIsEditing({ ...isEditing, redirectOnClick: e.target.checked })}
                          className="w-5 h-5 text-blue-600 rounded mt-0.5"
                        />
                        <div>
                          <span className="block text-sm font-semibold text-gray-900">Redirect on click</span>
                          <span className="block text-xs text-gray-600">Cliccando la card sul portale pubblico, il browser apre direttamente l&rsquo;URL senza mostrare il modale.</span>
                        </div>
                      </label>
                    </div>
                  </>
                ) : (
                  <div>
                    <label className="block text-sm font-semibold text-gray-800 mb-1">Short Description</label>
                    <textarea value={isEditing.shortDescription || ''} onChange={e => setIsEditing({ ...isEditing, shortDescription: e.target.value })} className={`${inputClasses} h-24 resize-none`} placeholder="Brief summary..." />
                  </div>
                )}
                {isEditing.cardType !== 'BOOK' && (
                <div className="bg-gray-50 p-3 rounded-lg border border-gray-200 space-y-3">
                  <label className="flex items-start gap-3 cursor-pointer">
                    <input
                      type="checkbox"
                      checked={!!isEditing.autoFullscreen}
                      onChange={e => setIsEditing({ ...isEditing, autoFullscreen: e.target.checked })}
                      className="w-5 h-5 text-blue-600 rounded mt-0.5"
                    />
                    <div>
                      <span className="block text-sm font-semibold text-gray-900">Auto fullscreen</span>
                      <span className="block text-xs text-gray-600">Open the gallery in fullscreen immediately when the user clicks this card.</span>
                    </div>
                  </label>
                  <label className="flex items-start gap-3 cursor-pointer">
                    <input
                      type="checkbox"
                      checked={!!isEditing.skipPreview}
                      onChange={e => setIsEditing({ ...isEditing, skipPreview: e.target.checked })}
                      className="w-5 h-5 text-blue-600 rounded mt-0.5"
                    />
                    <div>
                      <span className="block text-sm font-semibold text-gray-900">Cover not in the gallery</span>
                      <span className="block text-xs text-gray-600">The cover stays as the card thumbnail only. Combine with &ldquo;Auto fullscreen&rdquo; to jump straight into the gallery items.</span>
                    </div>
                  </label>
                </div>
                )}
              </div>
              <div className="space-y-5">
                {/* Cover Image */}
                <div>
                  <label className="block text-sm font-semibold text-gray-800 mb-1">
                    Cover Image <span className="text-gray-400 font-normal text-xs">(shown in grid)</span>
                  </label>
                  <div className="border-2 border-dashed border-gray-300 rounded-lg p-3 hover:bg-gray-50 transition-colors">
                    <input type="file" accept="image/*" onChange={e => handleUpload(e, 'imageUrl')} className="block w-full text-sm text-gray-700 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100 cursor-pointer" />
                    {uploading['imageUrl'] && <p className="mt-2 text-sm text-blue-600 font-medium">Uploading...</p>}
                  </div>
                  {isEditing.imageUrl && (
                    <div className="mt-3 relative rounded-lg overflow-hidden border border-gray-200 group">
                      <img src={isEditing.imageUrl} className="w-full h-32 object-cover" alt="Cover preview" />
                      <a
                        href={isEditing.imageUrl}
                        download={extractFileName(isEditing.imageUrl)}
                        className="absolute top-2 right-12 bg-gray-700 hover:bg-gray-800 text-white w-8 h-8 rounded-full text-sm font-bold shadow opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
                        title="Scarica"
                        aria-label="Scarica cover"
                      >⬇</a>
                      <button
                        onClick={() => setIsEditing({...isEditing, imageUrl: ''})}
                        className="absolute top-2 right-2 bg-red-500 text-white w-8 h-8 rounded-full text-sm font-bold shadow opacity-0 group-hover:opacity-100 transition-opacity hover:bg-red-600"
                        title="Remove cover image"
                      >✕</button>
                    </div>
                  )}
                </div>

                {/* Gallery Media (images + videos + PDFs) — nascosta per INFO_PAGE (solo cover ammessa) */}
                {isEditing.cardType !== 'INFO_PAGE' && (
                <div>
                  <label className="block text-sm font-semibold text-gray-800 mb-1">
                    Gallery Media <span className="text-gray-400 font-normal text-xs">(images, videos or PDFs — PDF pages become images)</span>
                  </label>
                  <div className="border-2 border-dashed border-gray-300 rounded-lg p-3 hover:bg-gray-50 transition-colors">
                    <input
                      type="file"
                      accept="image/*,video/*,application/pdf,.pdf,.mov,.qt,.mkv,.avi,.divx,.wmv,.asf,.flv,.f4v,.3gp,.3gpp,.3g2,.mts,.m2ts,.ts,.mpg,.mpeg,.vob,.mxf,.ogv,.ogg"
                      multiple
                      onChange={handleUploadExtraMedia}
                      className="block w-full text-sm text-gray-700 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-purple-50 file:text-purple-700 hover:file:bg-purple-100 cursor-pointer"
                    />
                    {uploading['extraMedia'] && !pdfProgress && <p className="mt-2 text-sm text-purple-600 font-medium">Uploading...</p>}
                    {pdfProgress && (
                      <p className="mt-2 text-sm text-purple-600 font-medium">
                        Processing &ldquo;{pdfProgress.name}&rdquo;: page {pdfProgress.page} of {pdfProgress.total}
                      </p>
                    )}
                  </div>

                  {(isEditing.extraMedia || []).length > 0 && (
                    <div className="mt-3 space-y-2">
                      {(isEditing.extraMedia || []).map((item, i) => {
                        const video = isVideoUrl(item.url);
                        return (
                          <div key={item.url + i} className="flex items-center gap-3 p-2 bg-gray-50 border border-gray-200 rounded-lg">
                            <div className="relative w-16 h-16 rounded-md overflow-hidden bg-black shrink-0">
                              {video ? (
                                <>
                                  <video src={item.url} className="w-full h-full object-cover" muted preload="metadata" />
                                  <div className="absolute inset-0 flex items-center justify-center bg-black/30 text-white text-xl">▶</div>
                                </>
                              ) : (
                                <img src={item.url} className="w-full h-full object-cover" alt="" />
                              )}
                              <span className="absolute bottom-0 left-0 right-0 text-center text-white text-[10px] bg-black/60">{i + 1}</span>
                            </div>
                            <div className="flex-1 min-w-0">
                              <div className="text-xs font-semibold text-gray-700 uppercase tracking-wider">
                                {video ? 'Video' : 'Image'}
                              </div>
                              {video && (
                                <div className="mt-1 flex flex-wrap gap-x-4 gap-y-1">
                                  <label className="flex items-center gap-2 cursor-pointer">
                                    <input
                                      type="checkbox"
                                      checked={!!item.autoplay}
                                      onChange={() => toggleAutoplay(i)}
                                      className="w-4 h-4 text-blue-600 rounded"
                                    />
                                    <span className="text-sm text-gray-700">Autoplay</span>
                                  </label>
                                  <label className="flex items-center gap-2 cursor-pointer">
                                    <input
                                      type="checkbox"
                                      checked={!!item.muted}
                                      onChange={() => toggleMuted(i)}
                                      className="w-4 h-4 text-blue-600 rounded"
                                    />
                                    <span className="text-sm text-gray-700">Muted</span>
                                  </label>
                                </div>
                              )}
                            </div>
                            <button
                              onClick={() => moveExtraMedia(i, 'up')}
                              className="p-2 text-gray-500 hover:text-gray-800 hover:bg-gray-200 rounded shrink-0 disabled:opacity-40 disabled:cursor-not-allowed focus:outline-none focus-visible:outline-none"
                              title="Sposta su"
                              aria-label="Sposta su"
                              disabled={i === 0}
                            >↑</button>
                            <button
                              onClick={() => moveExtraMedia(i, 'down')}
                              className="p-2 text-gray-500 hover:text-gray-800 hover:bg-gray-200 rounded shrink-0 disabled:opacity-40 disabled:cursor-not-allowed focus:outline-none focus-visible:outline-none"
                              title="Sposta giù"
                              aria-label="Sposta giù"
                              disabled={i === (isEditing.extraMedia || []).length - 1}
                            >↓</button>
                            <a
                              href={item.url}
                              download={extractFileName(item.url)}
                              className="bg-gray-500 hover:bg-gray-600 text-white w-8 h-8 rounded-full text-sm font-bold shrink-0 flex items-center justify-center"
                              title="Scarica"
                              aria-label="Scarica"
                            >⬇</a>
                            <button
                              onClick={() => removeExtraMedia(i)}
                              className="bg-red-500 hover:bg-red-600 text-white w-8 h-8 rounded-full text-sm font-bold shrink-0"
                              title="Remove"
                            >✕</button>
                          </div>
                        );
                      })}
                    </div>
                  )}
                </div>
                )}
              </div>
            </div>

            <div className="flex gap-3 pt-8 mt-6 border-t border-gray-200 justify-end">
              <button onClick={() => setIsEditing(null)} className="px-5 py-2.5 text-gray-700 hover:bg-gray-100 rounded-lg font-medium transition-colors">
                Cancel
              </button>
              <button onClick={handleSaveCard} className="bg-green-600 text-white px-8 py-2.5 rounded-lg hover:bg-green-700 font-medium shadow-sm transition-colors">
                Save Card
              </button>
            </div>
          </div>
        </div>
      )}
	  
	  {/* CUSTOM CONFIRM DIALOG */}
      {confirmDialog && (
        <div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in duration-200">
          <div className="bg-white rounded-xl shadow-2xl p-6 max-w-sm w-full animate-in zoom-in-95">
            <h3 className="text-xl font-bold text-gray-900 mb-2">Confirm Action</h3>
            <p className="text-gray-600 mb-6 leading-relaxed">{confirmDialog.message}</p>
            <div className="flex justify-end gap-3">
              <button 
                onClick={() => setConfirmDialog(null)} 
                className="px-4 py-2.5 text-gray-700 hover:bg-gray-100 rounded-lg font-medium transition-colors"
              >
                Cancel
              </button>
              <button 
                onClick={confirmDialog.onConfirm} 
                className="px-6 py-2.5 bg-red-600 text-white rounded-lg hover:bg-red-700 font-medium transition-colors shadow-sm"
              >
                Delete
              </button>
            </div>
          </div>
        </div>
      )}

      {/* CUSTOM TOAST NOTIFICATION */}
      {toast && (
        <div className={`fixed bottom-6 right-6 z-[70] text-white px-6 py-4 rounded-lg shadow-2xl flex items-start gap-3 animate-in slide-in-from-bottom-5 fade-in duration-300 max-w-md ${
          toast.type === 'error' ? 'bg-red-700' : 'bg-gray-900'
        }`}>
          <div className={`w-6 h-6 rounded-full flex items-center justify-center font-bold text-sm shrink-0 mt-0.5 ${
            toast.type === 'error' ? 'bg-white text-red-700' : 'bg-green-500 text-gray-900'
          }`}>
            {toast.type === 'error' ? '!' : '✓'}
          </div>
          <span className="font-medium leading-snug">{toast.message}</span>
        </div>
      )}

    </div>
  );
}