import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { useTranslation } from '@/hooks/use-translation';
import { cn } from '@/lib/utils';
import axios from 'axios';
import { AlertTriangle, Camera, ChevronRight, Eye, EyeOff, Loader2, Pencil, Plus, RotateCcw, Trash2 } from 'lucide-react';
import type { FormEvent } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { toast } from 'sonner';
import {
    countSteps,
    droppedCustom,
    groupRows,
    newCustomId,
    pick,
    readSettings,
    sameSettings,
    sharedSettings,
    toggleHidden,
    toggleOpen,
    togglePhoto,
    type CleaningSettings,
    type CustomStep,
    type PlanGroup,
    type RoomCount,
    type StepRow,
} from '../cleaning-checklist';

interface Logement {
    id: number;
    name?: string | null;
    internal_name?: string | null;
}

interface Props {
    logement: Logement;
    /** Prévient le wizard qu'un réglage est enregistré (pastille de l'étape). */
    onSaved?: () => void;
    /** Ouvre l'étape « Commodités », d'où viennent les pièces. */
    onEditRooms?: () => void;
}

interface Plan {
    groups: PlanGroup[];
    count: number;
    photos: number;
}

interface ChecklistResponse {
    source: 'property' | 'standard';
    rooms: RoomCount[];
    setup: Partial<CleaningSettings>;
    plan: Plan;
    upcoming?: number;
    refreshed?: number;
}

const LABEL_MAX = 120;
const DETAILS_MAX = 500;

/**
 * « Pièces pour le ménage » — étape optionnelle du wizard d'édition (jalon B).
 *
 * Les pièces sont celles de la fiche (« Commodités » et « Informations
 * générales ») : l'étape les montre, ne les saisit pas. Le bureau y règle la
 * check-list sur mesure — masquer une étape, exiger ou retirer une photo,
 * ajouter une étape —, des gestes qui se lisent directement dans le
 * formulaire. Chaque étape est un accordéon : son libellé, et ses détails à
 * déplier. Rien ne part avant « Enregistrer », qui rafraîchit aussi les
 * ménages à venir pas commencés.
 *
 * Endpoints JSON en axios, comme les instructions d'entrée : jamais de reload
 * Inertia de logements/edit, jamais Rentals United.
 */
export default function CleaningChecklistSection({ logement, onSaved, onEditRooms }: Props) {
    const { t, locale } = useTranslation();
    const tr = (key: string, fallback: string) => t('properties.cleaning-checklist', key, fallback);

    const [rooms, setRooms] = useState<RoomCount[]>([]);
    const [source, setSource] = useState<'property' | 'standard'>('property');
    const [settings, setSettings] = useState<CleaningSettings | null>(null);
    const [saved, setSaved] = useState<CleaningSettings | null>(null);
    const [groups, setGroups] = useState<PlanGroup[]>([]);
    const [upcoming, setUpcoming] = useState(0);
    const [loadError, setLoadError] = useState(false);
    const [saving, setSaving] = useState(false);
    const [serverErrors, setServerErrors] = useState<string[]>([]);
    /** Les étapes dépliées (accordéon), par référence. */
    const [open, setOpen] = useState<Set<string>>(new Set());
    /**
     * Le formulaire d'étape ouvert : dans quel groupe, pour quelle étape — ou,
     * pour une nouvelle (`id` null), l'identifiant qu'elle prendra.
     */
    const [editing, setEditing] = useState<{ group: string; id: string | null; draftId?: string } | null>(null);

    const apply = useCallback((data: ChecklistResponse) => {
        const next = readSettings(data.setup);
        setRooms(data.rooms);
        setSource(data.source);
        setSettings(next);
        setSaved(next);
        setGroups(data.plan.groups);
    }, []);

    const load = useCallback(async () => {
        setLoadError(false);
        try {
            const res = await axios.get<ChecklistResponse>(route('logements.cleaning-checklist.show', logement.id));
            apply(res.data);
            setUpcoming(res.data.upcoming ?? 0);
        } catch {
            setLoadError(true);
        }
    }, [apply, logement.id]);

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

    const totals = useMemo(() => (settings ? countSteps(groups, settings) : { steps: 0, photos: 0 }), [groups, settings]);
    const dropped = useMemo(() => (settings ? droppedCustom(groups, settings) : []), [groups, settings]);
    const dirty = settings !== null && saved !== null && !sameSettings(settings, saved);

    if (loadError) {
        return (
            <div className="flex flex-col items-start gap-3">
                <p className="text-destructive text-sm">{tr('load_error', 'Impossible de charger la check-list du ménage.')}</p>
                <Button type="button" variant="outline" onClick={() => void load()}>
                    {tr('retry', 'Réessayer')}
                </Button>
            </div>
        );
    }

    if (!settings) {
        return (
            <div className="text-muted-foreground flex items-center gap-2 py-12 text-sm">
                <Loader2 className="h-4 w-4 animate-spin" />
            </div>
        );
    }

    const upsertCustom = (step: CustomStep) => {
        const exists = settings.custom.some((c) => c.id === step.id);
        setSettings({ ...settings, custom: exists ? settings.custom.map((c) => (c.id === step.id ? step : c)) : [...settings.custom, step] });
        setEditing(null);
    };

    const removeCustom = (id: string) => setSettings({ ...settings, custom: settings.custom.filter((c) => c.id !== id) });

    const reset = () => {
        setSettings(sharedSettings());
        setEditing(null);
    };

    async function save() {
        if (!settings) return;
        setSaving(true);
        setServerErrors([]);
        try {
            const res = await axios.patch<ChecklistResponse>(route('logements.cleaning-checklist.update', logement.id), settings);
            apply(res.data);
            const refreshed = res.data.refreshed ?? 0;
            toast.success(
                refreshed > 0
                    ? tr('saved_refreshed', 'Check-list enregistrée · :count ménage(s) à venir mis à jour.').replace(':count', String(refreshed))
                    : tr('saved', 'Check-list enregistrée.'),
            );
            onSaved?.();
        } catch (e) {
            const resp = (e as { response?: { status?: number; data?: { errors?: Record<string, string[]> } } }).response;
            if (resp?.status === 422 && resp.data?.errors) {
                setServerErrors(Object.values(resp.data.errors).map((messages) => messages[0]));
            } else {
                toast.error(tr('save_error', 'Erreur lors de l’enregistrement.'));
            }
        } finally {
            setSaving(false);
        }
    }

    return (
        <div className="flex flex-col gap-6">
            <h2 className="text-primary text-xl font-semibold">{tr('page_title', 'Pièces pour le ménage')}</h2>

            {/* Pièces : celles de la fiche, en lecture seule */}
            <Card>
                <CardHeader>
                    <div className="flex flex-wrap items-center justify-between gap-2">
                        <CardTitle>{tr('rooms_section', 'Pièces du logement')}</CardTitle>
                        <Badge variant={source === 'property' ? 'secondary' : 'outline'}>
                            {source === 'property'
                                ? tr('source_property', 'Pièces de la fiche logement')
                                : tr('source_standard', 'Modèle standard : aucune pièce n’est déclarée dans « Commodités »')}
                        </Badge>
                    </div>
                    <CardDescription>{tr('rooms_hint', '')}</CardDescription>
                </CardHeader>
                <CardContent className="space-y-3">
                    <div className="flex flex-wrap gap-2">
                        {rooms
                            .filter((room) => room.count > 0)
                            .map((room) => (
                                <span key={room.kind} className="bg-muted/40 inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-sm">
                                    {pick(room.label, locale)}
                                    {room.count > 1 && <span className="font-semibold tabular-nums">× {room.count}</span>}
                                </span>
                            ))}
                    </div>
                    <p className="text-muted-foreground text-xs">{tr('entrance_hint', '')}</p>
                    {onEditRooms && (
                        <Button type="button" variant="outline" size="sm" onClick={onEditRooms}>
                            <Pencil className="mr-1 h-4 w-4" />
                            {tr('edit_rooms', 'Modifier les commodités')}
                        </Button>
                    )}
                </CardContent>
            </Card>

            {/* Check-list : un accordéon par étape */}
            <Card>
                <CardHeader>
                    <div className="flex flex-wrap items-center justify-between gap-2">
                        <CardTitle>{tr('checklist_section', 'Check-list du ménage')}</CardTitle>
                        <div className="text-muted-foreground flex items-center gap-2 text-sm">
                            <span className="text-foreground font-semibold">
                                {tr('steps_count', ':count étapes').replace(':count', String(totals.steps))}
                            </span>
                            <span>·</span>
                            <span>{tr('photos_count', ':count avec photo').replace(':count', String(totals.photos))}</span>
                        </div>
                    </div>
                    <CardDescription>{tr('checklist_hint', '')}</CardDescription>
                </CardHeader>
                <CardContent className="space-y-4">
                    {dropped.length > 0 && (
                        <div className="flex items-start gap-2 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:border-amber-700 dark:bg-amber-950/40 dark:text-amber-200">
                            <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
                            <span>
                                {tr(
                                    'dropped_custom',
                                    ':count étape(s) ajoutée(s) dans une pièce qui n’existe plus seront supprimées à l’enregistrement.',
                                ).replace(':count', String(dropped.length))}
                            </span>
                        </div>
                    )}

                    {groups.map((group) => {
                        const rows = groupRows(group, settings);
                        const visible = rows.filter((row) => !row.hidden).length;
                        return (
                            <div key={group.key} className="overflow-hidden rounded-lg border">
                                <div className="bg-muted/40 flex items-center justify-between border-b px-3 py-2">
                                    <span className="text-muted-foreground text-xs font-semibold tracking-wide uppercase">
                                        {pick(group.label, locale)}
                                    </span>
                                    <span className="text-muted-foreground text-xs tabular-nums">{visible}</span>
                                </div>
                                <ul className="divide-y">
                                    {rows.map((row) =>
                                        editing !== null && editing.id !== null && editing.id === row.custom ? (
                                            <li key={row.ref} className="px-3 py-3">
                                                <CustomStepForm
                                                    tr={tr}
                                                    initial={settings.custom.find((c) => c.id === row.custom)!}
                                                    onSubmit={upsertCustom}
                                                    onCancel={() => setEditing(null)}
                                                />
                                            </li>
                                        ) : (
                                            <StepLine
                                                key={row.ref}
                                                row={row}
                                                open={open.has(row.ref)}
                                                locale={locale}
                                                tr={tr}
                                                onToggleOpen={() => setOpen(toggleOpen(open, row.ref))}
                                                onTogglePhoto={() =>
                                                    setSettings(
                                                        togglePhoto(
                                                            settings,
                                                            row,
                                                            group.steps.find((step) => step.ref === row.ref)?.photo_default ?? false,
                                                        ),
                                                    )
                                                }
                                                onToggleHidden={() => setSettings(toggleHidden(settings, row.ref))}
                                                onEdit={() => setEditing({ group: group.key, id: row.custom })}
                                                onDelete={() => row.custom && removeCustom(row.custom)}
                                            />
                                        ),
                                    )}
                                </ul>
                                <div className="border-t px-3 py-2">
                                    {editing?.group === group.key && editing.id === null ? (
                                        <CustomStepForm
                                            tr={tr}
                                            initial={{
                                                id: editing.draftId ?? newCustomId(settings.custom.map((c) => c.id)),
                                                group: group.key,
                                                label: '',
                                                details: null,
                                                photo: false,
                                            }}
                                            isNew
                                            onSubmit={upsertCustom}
                                            onCancel={() => setEditing(null)}
                                        />
                                    ) : (
                                        <Button
                                            type="button"
                                            variant="ghost"
                                            size="sm"
                                            onClick={() =>
                                                setEditing({ group: group.key, id: null, draftId: newCustomId(settings.custom.map((c) => c.id)) })
                                            }
                                        >
                                            <Plus className="mr-1 h-4 w-4" />
                                            {tr('add_step', 'Ajouter une étape')}
                                        </Button>
                                    )}
                                </div>
                            </div>
                        );
                    })}
                </CardContent>
            </Card>

            {serverErrors.length > 0 && (
                <ul className="text-destructive list-disc space-y-1 pl-5 text-sm">
                    {serverErrors.map((message, index) => (
                        <li key={index}>{message}</li>
                    ))}
                </ul>
            )}

            <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                <p className="text-muted-foreground text-sm">
                    {upcoming > 0 &&
                        tr(
                            'upcoming_hint',
                            ':count ménage(s) à venir, pas encore commencé(s), recevront cette check-list à l’enregistrement.',
                        ).replace(':count', String(upcoming))}
                </p>
                <div className="flex flex-wrap items-center justify-end gap-2">
                    {dirty && <span className="text-xs font-medium text-amber-600">{tr('unsaved', 'Modifications non enregistrées')}</span>}
                    <Button type="button" variant="outline" onClick={reset}>
                        <RotateCcw className="mr-1 h-4 w-4" />
                        {tr('reset', 'Revenir à la check-list commune')}
                    </Button>
                    <Button type="button" onClick={() => void save()} disabled={saving || !dirty}>
                        {saving ? tr('saving', 'Enregistrement…') : tr('save', 'Enregistrer')}
                    </Button>
                </div>
            </div>
        </div>
    );
}

type Tr = (key: string, fallback: string) => string;

/** Une étape : son libellé, qui déplie ses détails, et ses réglages. */
function StepLine({
    row,
    open,
    locale,
    tr,
    onToggleOpen,
    onTogglePhoto,
    onToggleHidden,
    onEdit,
    onDelete,
}: {
    row: StepRow;
    open: boolean;
    locale: string;
    tr: Tr;
    onToggleOpen: () => void;
    onTogglePhoto: () => void;
    onToggleHidden: () => void;
    onEdit: () => void;
    onDelete: () => void;
}) {
    const details = pick(row.details, locale);
    const hasDetails = details !== '';
    return (
        <li className={cn(row.hidden && 'bg-muted/30')}>
            <div className="flex items-center gap-2 px-3 py-2">
                <button
                    type="button"
                    className={cn('flex min-w-0 flex-1 items-center gap-2 text-left', !hasDetails && 'cursor-default', row.hidden && 'opacity-60')}
                    onClick={hasDetails ? onToggleOpen : undefined}
                    aria-expanded={hasDetails ? open : undefined}
                    title={hasDetails ? (open ? tr('hide_details', 'Masquer les détails') : tr('show_details', 'Voir les détails')) : undefined}
                >
                    {hasDetails ? (
                        <ChevronRight className={cn('text-muted-foreground h-4 w-4 shrink-0 transition-transform', open && 'rotate-90')} />
                    ) : (
                        <span className="w-4 shrink-0" />
                    )}
                    <span className={cn('text-sm font-medium', row.hidden && 'line-through')}>{pick(row.label, locale)}</span>
                    {row.custom !== null && <Badge variant="secondary">{tr('added', 'ajoutée')}</Badge>}
                    {row.hidden && <Badge variant="outline">{tr('hidden', 'masquée')}</Badge>}
                </button>
                <div className="flex shrink-0 items-center gap-1">
                    <Button
                        type="button"
                        variant={row.photo ? 'secondary' : 'ghost'}
                        size="sm"
                        className={cn('h-8 px-2', !row.photo && 'text-muted-foreground')}
                        disabled={row.hidden}
                        aria-pressed={row.photo}
                        title={row.photo ? tr('remove_photo', 'Ne plus exiger de photo') : tr('require_photo', 'Exiger une photo')}
                        onClick={onTogglePhoto}
                    >
                        <Camera className="h-4 w-4" />
                        {row.photo && <span className="ml-1 text-xs">{tr('photo_required', 'Photo')}</span>}
                    </Button>
                    {row.custom !== null ? (
                        <>
                            <Button
                                type="button"
                                variant="ghost"
                                size="icon"
                                className="h-8 w-8"
                                title={tr('custom_edit', 'Modifier cette étape')}
                                onClick={onEdit}
                            >
                                <Pencil className="h-4 w-4" />
                            </Button>
                            <Button
                                type="button"
                                variant="ghost"
                                size="icon"
                                className="text-destructive hover:bg-destructive/10 h-8 w-8"
                                title={tr('custom_delete', 'Supprimer cette étape')}
                                onClick={onDelete}
                            >
                                <Trash2 className="h-4 w-4" />
                            </Button>
                        </>
                    ) : (
                        <Button
                            type="button"
                            variant="ghost"
                            size="icon"
                            className="h-8 w-8"
                            title={row.hidden ? tr('show_step', 'Rétablir cette étape') : tr('hide_step', 'Masquer cette étape')}
                            aria-pressed={row.hidden}
                            onClick={onToggleHidden}
                        >
                            {row.hidden ? <Eye className="h-4 w-4" /> : <EyeOff className="h-4 w-4" />}
                        </Button>
                    )}
                </div>
            </div>
            {hasDetails && open && (
                <p className={cn('text-muted-foreground px-3 pb-3 pl-9 text-xs leading-relaxed', row.hidden && 'opacity-60')}>{details}</p>
            )}
        </li>
    );
}

function CustomStepForm({
    tr,
    initial,
    isNew = false,
    onSubmit,
    onCancel,
}: {
    tr: Tr;
    initial: CustomStep;
    isNew?: boolean;
    onSubmit: (step: CustomStep) => void;
    onCancel: () => void;
}) {
    const [label, setLabel] = useState(initial.label);
    const [details, setDetails] = useState(initial.details ?? '');
    const [photo, setPhoto] = useState(initial.photo);
    const [error, setError] = useState<string | null>(null);

    function submit(event: FormEvent) {
        event.preventDefault();
        event.stopPropagation();
        const trimmed = label.trim();
        if (trimmed === '') {
            setError(tr('error_custom_label', 'Le libellé de l’étape est obligatoire.'));
            return;
        }
        onSubmit({ ...initial, label: trimmed, details: details.trim() === '' ? null : details.trim(), photo });
    }

    return (
        <form onSubmit={submit} className="space-y-3">
            <div>
                <Label htmlFor={`cc_label_${initial.id}`}>{tr('custom_label', 'Libellé')}</Label>
                <Input
                    id={`cc_label_${initial.id}`}
                    value={label}
                    maxLength={LABEL_MAX}
                    autoFocus
                    onChange={(e) => {
                        setLabel(e.target.value);
                        setError(null);
                    }}
                    placeholder={tr('custom_label_placeholder', 'Ex. Arroser les plantes')}
                    className="mt-1"
                />
                {error && <p className="text-destructive mt-1 text-sm">{error}</p>}
            </div>
            <div>
                <Label htmlFor={`cc_details_${initial.id}`}>{tr('custom_details', 'Détails (facultatif)')}</Label>
                <Textarea
                    id={`cc_details_${initial.id}`}
                    value={details}
                    maxLength={DETAILS_MAX}
                    rows={2}
                    onChange={(e) => setDetails(e.target.value)}
                    placeholder={tr('custom_details_placeholder', 'Ce qu’il faut faire, précisément')}
                    className="mt-1"
                />
            </div>
            <div className="flex items-center gap-2">
                <Checkbox id={`cc_photo_${initial.id}`} checked={photo} onCheckedChange={(checked) => setPhoto(checked === true)} />
                <Label htmlFor={`cc_photo_${initial.id}`} className="text-sm font-normal">
                    {tr('custom_photo', 'Exiger une photo')}
                </Label>
            </div>
            <p className="text-muted-foreground text-xs">{tr('custom_french_only', 'En français : la traduction viendra plus tard.')}</p>
            <div className="flex justify-end gap-2">
                <Button type="button" variant="ghost" size="sm" onClick={onCancel}>
                    {tr('custom_cancel', 'Annuler')}
                </Button>
                <Button type="submit" size="sm">
                    {isNew ? tr('custom_add', 'Ajouter') : tr('custom_save', 'Valider')}
                </Button>
            </div>
        </form>
    );
}
