import { Head, Link, useForm, usePage } from '@inertiajs/react';
import type { FormEvent } from 'react';
import { useMemo, useState } from 'react';
import '../../../css/event-assignments.css';

type AssignableGuest = {
    id: string;
    name: string;
    isChild: boolean;
    assigned: boolean;
};

type AssignableGroup = {
    id: string;
    label: string;
    salutation: string | null;
    assigned: boolean;
    guests: AssignableGuest[];
};

type PageProps = {
    wedding: {
        name: string;
        timezone: string;
    } | null;
    event: {
        id: string;
        title: string;
        startsAt: string;
        venueName: string | null;
        isPublished: boolean;
    };
    groups: AssignableGroup[];
    summary: {
        invitedGroupCount: number;
        invitedGuestCount: number;
    };
    flash: {
        success: string | null;
    };
    errors: Record<string, string>;
};

type AssignmentPayload = {
    assignments: Array<{
        invitation_group_id: string;
        guest_ids: string[];
    }>;
};

const initialAssignments = (groups: AssignableGroup[]): AssignmentPayload['assignments'] =>
    groups.flatMap((group) => {
        const guestIds = group.guests.filter((guest) => guest.assigned).map((guest) => guest.id);

        return guestIds.length > 0
            ? [{ invitation_group_id: group.id, guest_ids: guestIds }]
            : [];
    });

const normalizeSearch = (value: string) => value.trim().toLocaleLowerCase('de-DE');

const formatEventDate = (value: string, timeZone: string) => {
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return 'Termin noch nicht verfügbar';

    try {
        return new Intl.DateTimeFormat('de-DE', {
            timeZone,
            weekday: 'long',
            day: '2-digit',
            month: 'long',
            year: 'numeric',
            hour: '2-digit',
            minute: '2-digit',
        }).format(date);
    } catch {
        return new Intl.DateTimeFormat('de-DE', {
            day: '2-digit',
            month: 'long',
            year: 'numeric',
            hour: '2-digit',
            minute: '2-digit',
        }).format(date);
    }
};

export default function EventAssignments() {
    const { wedding, event, groups, summary, flash, errors } = usePage<PageProps>().props;
    const [search, setSearch] = useState('');
    const form = useForm<AssignmentPayload>({ assignments: initialAssignments(groups) });
    const timeZone = wedding?.timezone || 'Europe/Berlin';

    const selectedGuestsByGroup = useMemo(
        () => new Map(form.data.assignments.map((assignment) => [assignment.invitation_group_id, new Set(assignment.guest_ids)])),
        [form.data.assignments],
    );

    const selectedGroupCount = form.data.assignments.length;
    const selectedGuestCount = form.data.assignments.reduce((total, assignment) => total + assignment.guest_ids.length, 0);
    const searchTerm = normalizeSearch(search);
    const filteredGroups = useMemo(() => {
        if (!searchTerm) return groups;

        return groups.filter((group) =>
            normalizeSearch(group.label).includes(searchTerm)
            || group.guests.some((guest) => normalizeSearch(guest.name).includes(searchTerm)),
        );
    }, [groups, searchTerm]);

    const assignmentErrors = useMemo(() => {
        const combinedErrors = { ...errors, ...(form.errors as Record<string, string>) };

        return [...new Set(Object.entries(combinedErrors)
            .filter(([key]) => key === 'assignments' || key.startsWith('assignments.'))
            .map(([, message]) => message)
            .filter(Boolean))];
    }, [errors, form.errors]);

    const generalError = errors.assignment ?? errors.event ?? errors.general;

    const setGroupGuests = (groupId: string, guestIds: string[]) => {
        const otherAssignments = form.data.assignments.filter(
            (assignment) => assignment.invitation_group_id !== groupId,
        );

        form.setData('assignments', guestIds.length > 0
            ? [...otherAssignments, { invitation_group_id: groupId, guest_ids: guestIds }]
            : otherAssignments);
    };

    const toggleGroup = (group: AssignableGroup) => {
        const selectedIds = selectedGuestsByGroup.get(group.id);
        const allSelected = group.guests.length > 0 && group.guests.every((guest) => selectedIds?.has(guest.id));

        setGroupGuests(group.id, allSelected ? [] : group.guests.map((guest) => guest.id));
    };

    const toggleGuest = (groupId: string, guestId: string, checked: boolean) => {
        const currentIds = selectedGuestsByGroup.get(groupId) ?? new Set<string>();
        const nextIds = checked
            ? [...currentIds, guestId]
            : [...currentIds].filter((id) => id !== guestId);

        setGroupGuests(groupId, nextIds);
    };

    const submit = (submitEvent: FormEvent<HTMLFormElement>) => {
        submitEvent.preventDefault();
        if (form.processing) return;

        form.put(`/veranstaltungen/${event.id}/gaeste`, {
            preserveScroll: true,
        });
    };

    return (
        <>
            <Head title={`Gäste zuordnen · ${event.title}`} />
            <main className="assignment-shell">
                <nav className="assignment-nav" aria-label="Bereichsnavigation">
                    <div>
                        <span className="assignment-eyebrow">{wedding?.name ?? 'Unsere Hochzeit'}</span>
                        <strong>Gästezuordnung</strong>
                    </div>
                    <Link className="assignment-secondary-button" href="/veranstaltungen">Zurück zu Veranstaltungen</Link>
                </nav>

                <header className="assignment-event-card">
                    <div className="assignment-event-copy">
                        <span className={`assignment-event-status ${event.isPublished ? 'is-published' : 'is-draft'}`}>
                            <span aria-hidden="true" />
                            {event.isPublished ? 'Veröffentlicht' : 'Entwurf'}
                        </span>
                        <h1>{event.title}</h1>
                        <p>{formatEventDate(event.startsAt, timeZone)}</p>
                        {event.venueName && <p className="assignment-venue"><span aria-hidden="true">⌖</span>{event.venueName}</p>}
                    </div>
                    <div className="assignment-event-mark" aria-hidden="true">◇</div>
                </header>

                {flash.success && <div className="alert assignment-success" role="status">{flash.success}</div>}
                {generalError && <div className="alert alert-danger assignment-page-alert" role="alert">{generalError}</div>}

                <form aria-busy={form.processing} noValidate onSubmit={submit}>
                    <div className="assignment-layout">
                        <section className="assignment-picker" aria-labelledby="assignment-picker-title">
                            <div className="assignment-picker-heading">
                                <div>
                                    <span className="assignment-eyebrow">Einladungsliste</span>
                                    <h2 id="assignment-picker-title">Gruppen und Personen auswählen</h2>
                                </div>
                                {groups.length > 0 && <span>{groups.length} {groups.length === 1 ? 'Gruppe' : 'Gruppen'}</span>}
                            </div>

                            {groups.length > 0 && (
                                <div className="assignment-search">
                                    <label htmlFor="assignment-search">Gruppen oder Personen filtern</label>
                                    <div className="assignment-search-control">
                                        <span aria-hidden="true">⌕</span>
                                        <input
                                            autoComplete="off"
                                            id="assignment-search"
                                            onChange={(searchEvent) => setSearch(searchEvent.target.value)}
                                            placeholder="Name eingeben …"
                                            type="search"
                                            value={search}
                                        />
                                        {search && (
                                            <button aria-label="Suche leeren" onClick={() => setSearch('')} type="button">×</button>
                                        )}
                                    </div>
                                    <small aria-live="polite">
                                        {searchTerm && `${filteredGroups.length} von ${groups.length} Gruppen sichtbar`}
                                    </small>
                                </div>
                            )}

                            {assignmentErrors.length > 0 && (
                                <div className="alert alert-danger assignment-errors" role="alert">
                                    <strong>Die Auswahl konnte nicht gespeichert werden.</strong>
                                    <ul>
                                        {assignmentErrors.map((message) => <li key={message}>{message}</li>)}
                                    </ul>
                                </div>
                            )}

                            {groups.length === 0 ? (
                                <div className="assignment-empty">
                                    <span aria-hidden="true">◇</span>
                                    <h3>Noch keine Gästegruppen vorhanden</h3>
                                    <p>Lege zuerst Gästegruppen an. Danach kannst du einzelne Personen dieser Veranstaltung zuordnen.</p>
                                </div>
                            ) : filteredGroups.length === 0 ? (
                                <div className="assignment-empty is-filtered">
                                    <span aria-hidden="true">⌕</span>
                                    <h3>Keine passenden Namen</h3>
                                    <p>Prüfe den Suchbegriff. Deine bestehende Auswahl bleibt unverändert.</p>
                                    <button className="assignment-text-button" onClick={() => setSearch('')} type="button">Filter zurücksetzen</button>
                                </div>
                            ) : (
                                <div className="assignment-group-list">
                                    {filteredGroups.map((group) => {
                                        const selectedIds = selectedGuestsByGroup.get(group.id) ?? new Set<string>();
                                        const selectedCount = group.guests.filter((guest) => selectedIds.has(guest.id)).length;
                                        const allSelected = group.guests.length > 0 && selectedCount === group.guests.length;

                                        return (
                                            <fieldset className={`assignment-group-card${selectedCount > 0 ? ' is-selected' : ''}`} key={group.id}>
                                                <legend className="visually-hidden">{group.label}</legend>
                                                <div className="assignment-group-header">
                                                    <div>
                                                        <h3>{group.label}</h3>
                                                        {group.salutation && <p>{group.salutation}</p>}
                                                        <small>{selectedCount} von {group.guests.length} ausgewählt</small>
                                                    </div>
                                                    <button
                                                        aria-label={`${group.label} vollständig ${allSelected ? 'abwählen' : 'auswählen'}`}
                                                        className="assignment-group-toggle"
                                                        disabled={form.processing || group.guests.length === 0}
                                                        onClick={() => toggleGroup(group)}
                                                        type="button"
                                                    >{allSelected ? 'Alle abwählen' : 'Alle auswählen'}</button>
                                                </div>

                                                {group.guests.length === 0 ? (
                                                    <p className="assignment-no-guests">Diese Gruppe enthält noch keine Personen.</p>
                                                ) : (
                                                    <div className="assignment-guest-list">
                                                        {group.guests.map((guest) => {
                                                            const inputId = `assignment-${group.id}-${guest.id}`;
                                                            const checked = selectedIds.has(guest.id);

                                                            return (
                                                                <label className={`assignment-guest${checked ? ' is-selected' : ''}`} htmlFor={inputId} key={guest.id}>
                                                                    <input
                                                                        checked={checked}
                                                                        className="form-check-input"
                                                                        disabled={form.processing}
                                                                        id={inputId}
                                                                        onChange={(changeEvent) => toggleGuest(group.id, guest.id, changeEvent.target.checked)}
                                                                        type="checkbox"
                                                                    />
                                                                    <span className="assignment-guest-name">{guest.name}</span>
                                                                    {guest.isChild && <span className="assignment-child-badge">Kind</span>}
                                                                    <span className="assignment-check-mark" aria-hidden="true">✓</span>
                                                                </label>
                                                            );
                                                        })}
                                                    </div>
                                                )}
                                            </fieldset>
                                        );
                                    })}
                                </div>
                            )}
                        </section>

                        <aside className="assignment-summary" aria-labelledby="assignment-summary-title">
                            <span className="assignment-eyebrow">Aktuelle Auswahl</span>
                            <h2 id="assignment-summary-title">Einladungen</h2>
                            <div className="assignment-summary-counts" aria-live="polite">
                                <div><strong>{selectedGroupCount}</strong><span>{selectedGroupCount === 1 ? 'Gruppe' : 'Gruppen'}</span></div>
                                <div><strong>{selectedGuestCount}</strong><span>{selectedGuestCount === 1 ? 'Person' : 'Personen'}</span></div>
                            </div>
                            <p className="assignment-summary-note">
                                Gespeicherter Stand: {summary.invitedGroupCount} Gruppen und {summary.invitedGuestCount} Personen.
                            </p>
                            <button
                                className="assignment-primary-button"
                                disabled={form.processing || groups.length === 0}
                                type="submit"
                            >
                                {form.processing ? <><span className="spinner-border spinner-border-sm" aria-hidden="true" /> Wird gespeichert …</> : 'Zuordnung speichern'}
                            </button>
                            {form.processing && <p className="assignment-saving" role="status">Änderungen werden übertragen.</p>}
                        </aside>
                    </div>
                </form>
            </main>
        </>
    );
}
