import { Head, Link, router, useForm, usePage } from '@inertiajs/react';
import type { ChangeEvent, DragEvent, FormEvent, MouseEvent } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import '../../../css/photo-box.css';

type Photo = {
    id: string;
    eventId: string;
    eventTitle: string;
    uploaderName: string;
    caption: string | null;
    previewUrl: string;
    originalUrl: string;
    width: number;
    height: number;
    byteSize: number;
    createdAt: string;
    canEdit: boolean;
    canDelete: boolean;
};

type PageProps = {
    wedding: { name: string; timezone: string };
    events: Array<{ id: string; title: string; startsAt: string }>;
    selectedEventId: string | null;
    photos: Photo[];
    pagination: { currentPage: number; lastPage: number; previousPageUrl: string | null; nextPageUrl: string | null };
    permissions: { canUpload: boolean; canManage: boolean };
    limits: {
        maxFilesPerUpload: number;
        maxFileSizeMb: number;
        acceptedMimeTypes: string[];
        usedBytes: number | null;
        quotaBytes: number | null;
    };
    flash: { success: string | null };
    errors: Record<string, string>;
};

const supportedMimeTypes = ['image/jpeg', 'image/png', 'image/webp'];

const formatBytes = (bytes: number) => {
    if (bytes < 1024) return `${bytes} B`;
    if (bytes < 1024 ** 2) return `${(bytes / 1024).toLocaleString('de-DE', { maximumFractionDigits: 1 })} KB`;
    return `${(bytes / 1024 ** 2).toLocaleString('de-DE', { maximumFractionDigits: 1 })} MB`;
};

const formatDateTime = (value: string, timeZone: string) => {
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return 'Zeitpunkt nicht verfügbar';
    try {
        return new Intl.DateTimeFormat('de-DE', { timeZone, day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }).format(date);
    } catch {
        return new Intl.DateTimeFormat('de-DE', { dateStyle: 'medium', timeStyle: 'short' }).format(date);
    }
};

export default function PhotoBoxIndex() {
    const { wedding, events, selectedEventId, photos, pagination, permissions, limits, flash, errors } = usePage<PageProps>().props;
    const uploadForm = useForm<{ event_id: string; photos: File[] }>({ event_id: selectedEventId ?? events[0]?.id ?? '', photos: [] });
    const [previewUrls, setPreviewUrls] = useState<string[]>([]);
    const [fileInputKey, setFileInputKey] = useState(0);
    const [selectionErrors, setSelectionErrors] = useState<string[]>([]);
    const [isDragging, setIsDragging] = useState(false);
    const [selectedPhoto, setSelectedPhoto] = useState<Photo | null>(null);
    const [editingPhotoId, setEditingPhotoId] = useState<string | null>(null);
    const [captionDraft, setCaptionDraft] = useState('');
    const [pendingAction, setPendingAction] = useState<string | null>(null);
    const [captionErrorOwner, setCaptionErrorOwner] = useState<string | null>(null);
    const dialogRef = useRef<HTMLDialogElement>(null);
    const captionRef = useRef<HTMLTextAreaElement>(null);
    const allowedMimeTypes = supportedMimeTypes.filter((mime) => limits.acceptedMimeTypes.includes(mime));
    const maxFileBytes = limits.maxFileSizeMb * 1024 * 1024;
    const totalSelectedBytes = uploadForm.data.photos.reduce((total, file) => total + file.size, 0);
    const originalCaption = photos.find((photo) => photo.id === editingPhotoId)?.caption ?? '';
    const hasUnsavedCaption = editingPhotoId !== null && captionDraft !== originalCaption;
    const hasUnsavedChanges = uploadForm.data.photos.length > 0 || hasUnsavedCaption;
    const generalError = errors.photo_box ?? errors.photo ?? errors.general;
    const uploadErrors = [...new Set(Object.entries({ ...errors, ...(uploadForm.errors as Record<string, string>) }).filter(([key]) => key === 'photos' || key.startsWith('photos.')).map(([, message]) => message).filter(Boolean))];
    const captionError = errors.caption;

    useEffect(() => {
        const urls = uploadForm.data.photos.map((file) => URL.createObjectURL(file));
        setPreviewUrls(urls);
        return () => urls.forEach((url) => URL.revokeObjectURL(url));
    }, [uploadForm.data.photos]);

    useEffect(() => {
        if (selectedPhoto && dialogRef.current && !dialogRef.current.open) dialogRef.current.showModal();
    }, [selectedPhoto]);

    useEffect(() => {
        const warnBeforeUnload = (event: BeforeUnloadEvent) => {
            if (!hasUnsavedChanges) return;
            event.preventDefault();
            event.returnValue = '';
        };
        window.addEventListener('beforeunload', warnBeforeUnload);
        return () => window.removeEventListener('beforeunload', warnBeforeUnload);
    }, [hasUnsavedChanges]);

    const addFiles = (incoming: File[]) => {
        const messages: string[] = [];
        const remainingSlots = Math.max(0, limits.maxFilesPerUpload - uploadForm.data.photos.length);
        if (incoming.length > remainingSlots) messages.push(`Pro Upload sind höchstens ${limits.maxFilesPerUpload} Bilder möglich.`);
        const existingKeys = new Set(uploadForm.data.photos.map((file) => `${file.name}-${file.size}-${file.lastModified}`));
        const accepted: File[] = [];
        incoming.slice(0, remainingSlots).forEach((file) => {
            const key = `${file.name}-${file.size}-${file.lastModified}`;
            if (existingKeys.has(key)) return;
            if (!allowedMimeTypes.includes(file.type) || file.type === 'image/svg+xml') {
                messages.push(`${file.name}: Nur JPEG, PNG und WebP sind erlaubt.`);
                return;
            }
            if (file.size > maxFileBytes) {
                messages.push(`${file.name}: Die Datei ist größer als ${limits.maxFileSizeMb} MB.`);
                return;
            }
            existingKeys.add(key);
            accepted.push(file);
        });
        setSelectionErrors([...new Set(messages)]);
        if (accepted.length > 0) uploadForm.setData('photos', [...uploadForm.data.photos, ...accepted]);
    };

    const chooseFiles = (event: ChangeEvent<HTMLInputElement>) => {
        addFiles(Array.from(event.target.files ?? []));
        setFileInputKey((key) => key + 1);
    };

    const dropFiles = (event: DragEvent<HTMLDivElement>) => {
        event.preventDefault();
        setIsDragging(false);
        addFiles(Array.from(event.dataTransfer.files));
    };

    const removeSelectedFile = (index: number) => {
        uploadForm.setData('photos', uploadForm.data.photos.filter((_, fileIndex) => fileIndex !== index));
        setSelectionErrors([]);
    };

    const submitUpload = (event: FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        if (uploadForm.processing || uploadForm.data.photos.length === 0) return;
        uploadForm.post('/fotobox/fotos', {
            forceFormData: true,
            preserveScroll: true,
            onSuccess: () => { uploadForm.reset('photos'); setSelectionErrors([]); setFileInputKey((key) => key + 1); },
        });
    };

    const filterByEvent = (eventId: string) => {
        if (hasUnsavedChanges && !window.confirm('Nicht hochgeladene Bilder oder Bildunterschriften gehen beim Wechsel verloren. Fortfahren?')) return;
        uploadForm.setData('event_id', eventId || events[0]?.id || '');
        router.get('/fotobox', eventId ? { event: eventId } : {}, { preserveScroll: true });
    };

    const startCaptionEdit = (photo: Photo) => {
        setEditingPhotoId(photo.id);
        setCaptionDraft(photo.caption ?? '');
        setCaptionErrorOwner(null);
        window.setTimeout(() => captionRef.current?.focus(), 100);
    };

    const saveCaption = (photo: Photo) => {
        if (pendingAction) return;
        const action = `caption-${photo.id}`;
        setPendingAction(action);
        setCaptionErrorOwner(photo.id);
        router.put(`/fotobox/fotos/${photo.id}`, { caption: captionDraft }, {
            preserveScroll: true,
            onSuccess: () => { setEditingPhotoId(null); setCaptionErrorOwner(null); },
            onFinish: () => setPendingAction(null),
        });
    };

    const deletePhoto = (photo: Photo) => {
        if (pendingAction || !window.confirm('Möchtest du dieses Foto wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.')) return;
        const action = `delete-${photo.id}`;
        setPendingAction(action);
        router.delete(`/fotobox/fotos/${photo.id}`, {
            preserveScroll: true,
            onSuccess: () => { if (selectedPhoto?.id === photo.id) closeDialog(); if (editingPhotoId === photo.id) setEditingPhotoId(null); },
            onFinish: () => setPendingAction(null),
        });
    };

    const closeDialog = () => {
        dialogRef.current?.close();
        setSelectedPhoto(null);
    };

    const handleNavigation = (event: MouseEvent<Element>) => {
        if (hasUnsavedChanges && !window.confirm('Es gibt noch nicht hochgeladene Bilder oder eine ungespeicherte Bildunterschrift. Seite trotzdem verlassen?')) event.preventDefault();
    };

    const selectedEventTitle = events.find((event) => event.id === uploadForm.data.event_id)?.title;
    const quotaPercent = limits.usedBytes !== null && limits.quotaBytes
        ? Math.min(100, Math.round((limits.usedBytes / limits.quotaBytes) * 100))
        : null;

    return (
        <>
            <Head title="Fotobox" />
            <main className="photo-box-shell">
                <nav className="photo-box-nav" aria-label="Bereichsnavigation"><div><span className="photo-box-eyebrow">{wedding.name}</span><strong>Fotobox</strong></div><Link className="photo-box-secondary" href="/dashboard" onClick={handleNavigation}>Zurück zum Dashboard</Link></nav>
                <header className="photo-box-header"><div><span className="photo-box-eyebrow">Gemeinsame Erinnerungen</span><h1>Unsere Fotobox</h1><p>Teilt eure schönsten Schnappschüsse sicher mit den Gästen dieser Veranstaltung.</p></div>{events.length > 0 && <div className="photo-box-event-filter"><label htmlFor="photo-box-event">Veranstaltung</label><select className="form-select" id="photo-box-event" onChange={(event) => filterByEvent(event.target.value)} value={selectedEventId ?? ''}><option value="">Alle verfügbaren Veranstaltungen</option>{events.map((event) => <option key={event.id} value={event.id}>{event.title} · {formatDateTime(event.startsAt, wedding.timezone)}</option>)}</select></div>}</header>
                {flash.success && <div className="alert photo-box-success" role="status">{flash.success}</div>}{generalError && <div className="alert alert-danger photo-box-page-error" role="alert">{generalError}</div>}

                {events.length === 0 ? <section className="photo-box-empty"><span aria-hidden="true">◇</span><h2>Keine Fotobox verfügbar</h2><p>Momentan ist keine veröffentlichte, für euch freigegebene Fotobox-Veranstaltung vorhanden.</p></section> : <>
                    {permissions.canUpload && <section className="photo-box-upload-panel" aria-labelledby="photo-box-upload-title"><div className="photo-box-section-heading"><div><span className="photo-box-eyebrow">Bilder hinzufügen</span><h2 id="photo-box-upload-title">Erinnerungen hochladen</h2></div>{uploadForm.data.photos.length > 0 && <span>{uploadForm.data.photos.length}/{limits.maxFilesPerUpload}</span>}</div><form aria-busy={uploadForm.processing} noValidate onSubmit={submitUpload}><div className={`photo-box-drop-zone${isDragging ? ' is-dragging' : ''}`} onDragEnter={(event) => { event.preventDefault(); setIsDragging(true); }} onDragLeave={() => setIsDragging(false)} onDragOver={(event) => event.preventDefault()} onDrop={dropFiles}><span aria-hidden="true">◇</span><strong>Bilder hierher ziehen</strong><p>oder über den Dateidialog auswählen</p><label className="photo-box-primary" htmlFor="photo-box-files">Bilder auswählen</label><input accept={allowedMimeTypes.join(',')} id="photo-box-files" key={fileInputKey} multiple onChange={chooseFiles} type="file" /><small>JPEG, PNG oder WebP · maximal {limits.maxFileSizeMb} MB je Bild</small></div>
                        {(selectionErrors.length > 0 || uploadErrors.length > 0 || uploadForm.errors.event_id) && <div className="photo-box-file-errors" role="alert">{uploadForm.errors.event_id && <p>{uploadForm.errors.event_id}</p>}{[...selectionErrors, ...uploadErrors].map((message) => <p key={message}>{message}</p>)}</div>}
                        {uploadForm.data.photos.length > 0 && <><div className="photo-box-selected-summary"><strong>{uploadForm.data.photos.length} Bilder ausgewählt</strong><span>Gesamtgröße {formatBytes(totalSelectedBytes)}</span></div><div className="photo-box-selected-grid">{uploadForm.data.photos.map((file, index) => <article key={`${file.name}-${file.size}-${file.lastModified}`}><img alt={`Lokale Vorschau von ${file.name}`} src={previewUrls[index]} /><div><span>{file.name}</span><small>{formatBytes(file.size)}</small></div><button aria-label={`${file.name} aus Auswahl entfernen`} disabled={uploadForm.processing} onClick={() => removeSelectedFile(index)} type="button">×</button></article>)}</div></>}
                        {limits.usedBytes !== null && limits.quotaBytes !== null && <div className="photo-box-quota"><div><span>Speicher</span><strong>{formatBytes(limits.usedBytes)} von {formatBytes(limits.quotaBytes)}</strong></div><div aria-label={`${quotaPercent ?? 0} Prozent Speicher belegt`} role="progressbar" aria-valuemin={0} aria-valuemax={100} aria-valuenow={quotaPercent ?? 0}><span style={{ width: `${quotaPercent ?? 0}%` }} /></div></div>}
                        <div className="photo-box-upload-actions"><span>{selectedEventTitle ? `Upload für ${selectedEventTitle}` : 'Veranstaltung im Upload auswählen'}</span><button className="photo-box-primary" disabled={uploadForm.processing || uploadForm.data.photos.length === 0 || !uploadForm.data.event_id} type="submit">{uploadForm.processing ? `Upload ${uploadForm.progress?.percentage ?? 0} %` : 'Auswahl hochladen'}</button></div>{uploadForm.processing && <div className="photo-box-upload-progress" role="status"><span style={{ width: `${uploadForm.progress?.percentage ?? 0}%` }} />Bilder werden sicher übertragen.</div>}</form></section>}

                    <section className="photo-box-gallery-section" aria-labelledby="photo-box-gallery-title"><div className="photo-box-section-heading"><div><span className="photo-box-eyebrow">Galerie</span><h2 id="photo-box-gallery-title">Geteilte Momente</h2></div>{permissions.canManage && <span className="photo-box-manage-badge">Moderationsansicht</span>}</div>{photos.length === 0 ? <div className="photo-box-empty is-gallery"><span aria-hidden="true">◇</span><h3>Noch keine Fotos</h3><p>Das erste hochgeladene Bild eröffnet diese gemeinsame Galerie.</p></div> : <div className="photo-box-gallery">{photos.map((photo) => <article className="photo-box-card" key={photo.id}><button className="photo-box-image-button" aria-label={`Foto von ${photo.uploaderName} öffnen`} onClick={() => setSelectedPhoto(photo)} type="button"><img alt={photo.caption || `Foto von ${photo.uploaderName}`} height={photo.height} loading="lazy" src={photo.previewUrl} width={photo.width} /></button><div className="photo-box-card-body"><div><strong>{photo.uploaderName}</strong><span>{photo.eventTitle} · {formatDateTime(photo.createdAt, wedding.timezone)}</span></div>{editingPhotoId === photo.id ? <div className="photo-box-caption-editor"><label htmlFor={`photo-caption-${photo.id}`}>Bildunterschrift</label><textarea id={`photo-caption-${photo.id}`} onChange={(event) => setCaptionDraft(event.target.value)} ref={captionRef} rows={3} value={captionDraft} />{captionErrorOwner === photo.id && captionError && <p className="photo-box-field-error">{captionError}</p>}<div><button className="photo-box-text-button" disabled={pendingAction !== null} onClick={() => setEditingPhotoId(null)} type="button">Abbrechen</button><button className="photo-box-primary" disabled={pendingAction !== null} onClick={() => saveCaption(photo)} type="button">{pendingAction === `caption-${photo.id}` ? 'Wird gespeichert …' : 'Speichern'}</button></div></div> : photo.caption ? <p>{photo.caption}</p> : <p className="photo-box-no-caption">Keine Bildunterschrift</p>}<div className="photo-box-card-actions">{photo.canEdit && editingPhotoId !== photo.id && <button className="photo-box-text-button" disabled={pendingAction !== null} onClick={() => startCaptionEdit(photo)} type="button">Beschriftung bearbeiten</button>}{photo.canDelete && <button className="photo-box-text-button is-danger" disabled={pendingAction !== null} onClick={() => deletePhoto(photo)} type="button">{pendingAction === `delete-${photo.id}` ? 'Wird gelöscht …' : 'Löschen'}</button>}</div></div></article>)}</div>}
                        {pagination.lastPage > 1 && <nav className="photo-box-pagination" aria-label="Galerieseiten"><span>Seite {pagination.currentPage} von {pagination.lastPage}</span><div>{pagination.previousPageUrl ? <Link className="photo-box-secondary" href={pagination.previousPageUrl} onClick={handleNavigation} preserveScroll>Vorherige</Link> : <span>Vorherige</span>}{pagination.nextPageUrl ? <Link className="photo-box-secondary" href={pagination.nextPageUrl} onClick={handleNavigation} preserveScroll>Nächste</Link> : <span>Nächste</span>}</div></nav>}
                    </section>
                </>}

                {selectedPhoto && <dialog className="photo-box-dialog" onCancel={(event) => { event.preventDefault(); closeDialog(); }} onClose={() => setSelectedPhoto(null)} ref={dialogRef}><div className="photo-box-dialog-top"><div><strong>{selectedPhoto.uploaderName}</strong><span>{selectedPhoto.eventTitle}</span></div><button aria-label="Detailansicht schließen" onClick={closeDialog} type="button">×</button></div><img alt={selectedPhoto.caption || `Foto von ${selectedPhoto.uploaderName}`} src={selectedPhoto.previewUrl} />{selectedPhoto.caption && <p>{selectedPhoto.caption}</p>}<dl><div><dt>Aufgenommen</dt><dd>{formatDateTime(selectedPhoto.createdAt, wedding.timezone)}</dd></div><div><dt>Bildgröße</dt><dd>{selectedPhoto.width} × {selectedPhoto.height} px · {formatBytes(selectedPhoto.byteSize)}</dd></div></dl><a className="photo-box-primary" href={selectedPhoto.originalUrl} rel="noopener noreferrer" target="_blank">Original öffnen</a></dialog>}
            </main>
        </>
    );
}
