import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { useTranslation } from '@/hooks/use-translation';
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, router, usePage } from '@inertiajs/react';
import { ArrowLeft, ChevronLeft, ChevronRight } from 'lucide-react';
import {
    describeFingerprint,
    isRefusedLog,
    LOG_STATUS_VARIANTS,
    logStatusKind,
    REFUSED_BADGE_CLASSES,
    restoreSwitchNote,
    type LogStatusKind,
} from './lib/log-details';
import { FranchiseReadOnlyBanner, FranchiseSelect } from './components/FranchiseScope';
import { useYieldScope } from './hooks/useYieldPermissions';
import { formatCapturedAt } from './lib/push-preview';
import { scopeQuery } from './lib/yield-scope';

const NS = 'yield.index';

/**
 * Une nuit d'échantillon telle que les services l'écrivent dans `details.sample`
 * (les 5 premières nuits réellement envoyées ou photographiées).
 */
interface SampleNight {
    date?: string;
    price?: number | string;
}

/**
 * Le contenu de `details` varie selon l'action — trois formes, une par
 * producteur, et aucune n'est garantie : une ligne écrite avant l'ajout d'un
 * champ n'en portera jamais. Tout est donc optionnel et lu défensivement.
 */
interface YieldLogDetails {
    /** push — YieldPushJob */
    avg_price?: number | null;
    min_price?: number | null;
    max_price?: number | null;
    nights_pushed?: number;
    booked_nights_skipped?: number;
    /** ancien marqueur de refus (le motif), avant `refused` — cf. isRefusedLog */
    refus?: string;
    /** refus (préflight, ou photo refusée) — le statut reste `error` en base */
    refused?: boolean;
    reason?: string;
    kind?: string | null;
    nights_computed?: number;
    retry_after?: string;
    fingerprint?: Record<string, unknown>;
    /** restore — YieldPriceRestoreService */
    nights_restored?: number;
    skipped?: number;
    skipped_past?: number;
    skipped_beyond_window?: number;
    skipped_zero_price?: number;
    skipped_invalid?: number;
    nights_without_origin?: number;
    backup_nights?: number;
    backup_captured_at?: string | null;
    backup_kept?: boolean;
    yield_disabled?: boolean;
    yield_was_active?: boolean;
    restore_until?: string | null;
    /** backup — YieldPriceBackupService */
    nights_captured?: number;
    empty_origin?: boolean;
    /** backup (période photographiée) et push (première / dernière nuit envoyée) */
    from?: string | null;
    to?: string | null;
    /** commun */
    currency?: string;
    sample?: SampleNight[];
}

interface YieldLogEntry {
    id: number;
    yield_config_id: number | null;
    property_id: string | null;
    action: string;
    status: string;
    dates_count: number;
    details: YieldLogDetails | null;
    error: string | null;
    created_at: string;
    yield_config?: {
        id: number;
        property_id: string;
        internal_id: number;
    };
}

interface PaginatedLogs {
    data: YieldLogEntry[];
    current_page: number;
    last_page: number;
    per_page: number;
    total: number;
    next_page_url: string | null;
    prev_page_url: string | null;
}

interface PageProps {
    logs: PaginatedLogs;
    [key: string]: unknown;
}

/**
 * Statut TRADUIT. Un refus est `error` en base mais s'affiche « Refusé »
 * (cf. lib/log-details) : une panne se relance, un refus se corrige. La table
 * de libellés est statique à dessein — une clé construite à la volée
 * échapperait au contrôle des traductions.
 */
function StatusBadge({ status, details }: { status: string; details: YieldLogDetails | null }) {
    const { t } = useTranslation();

    const labels: Record<LogStatusKind, string> = {
        success: t(NS, 'logs_status_success', 'Succès'),
        error: t(NS, 'logs_status_error', 'Erreur'),
        dry_run: t(NS, 'logs_status_dry_run', 'Simulation'),
        refused: t(NS, 'logs_status_refused', 'Refusé'),
    };

    const kind = logStatusKind(status, details);

    if (kind === null) {
        return <Badge variant="secondary">{status}</Badge>;
    }

    return (
        <Badge variant={LOG_STATUS_VARIANTS[kind]} className={kind === 'refused' ? REFUSED_BADGE_CLASSES : undefined}>
            {labels[kind]}
        </Badge>
    );
}

/**
 * Les trois actions RÉELLEMENT écrites dans `yield_logs` : `push`
 * (YieldPushJob + app:yield-run), `backup` (la photo) et `restore` (le retour
 * aux anciens prix). Les entrées `preview` et `scrape` qui traînaient ici ont
 * été retirées : aucun code n'en écrit — le comparatif est une lecture sans le
 * moindre effet de bord, et le scraping n'existe pas. Un libellé pour une
 * action qui ne se produit jamais fait croire à un journal muet là où il n'y a
 * simplement rien à journaliser.
 */
function ActionBadge({ action }: { action: string }) {
    const { t } = useTranslation();

    const labels: Record<string, string> = {
        push: t(NS, 'logs_action_push', 'Push'),
        backup: t(NS, 'logs_action_backup', 'Sauvegarde'),
        restore: t(NS, 'logs_action_restore', 'Restauration'),
    };

    return <Badge variant="outline">{labels[action] ?? action}</Badge>;
}

/** '2026-09-03' ou '2026-09-03 14:22:01' → '03/09/2026'. */
function formatDay(value: string): string {
    const day = new Date(`${value.slice(0, 10)}T00:00:00`);

    return Number.isNaN(day.getTime()) ? value : day.toLocaleDateString('fr-FR');
}

/** Les 5 premières nuits envoyées — la preuve concrète de ce qui est parti. */
function SampleLine({ sample }: { sample: SampleNight[] }) {
    const { t } = useTranslation();

    const nights = sample.filter((night) => typeof night.date === 'string');

    if (nights.length === 0) {
        return null;
    }

    return (
        <div className="text-muted-foreground text-xs">
            {t(NS, 'logs_sample', 'Exemple')} :{' '}
            {nights
                .map((night) => `${formatDay(night.date as string)} → ${night.price ?? '—'}`)
                .join(' · ')}
        </div>
    );
}

/**
 * Ce que la restauration a rendu — et ce qu'elle n'a PAS rendu, motif par
 * motif. C'est la question que se pose l'exploitant quand la photo annonce 366
 * nuits et que 274 seulement repartent : la photo a-t-elle vieilli, ou
 * contient-elle des prix nuls ?
 */
function RestoreDetails({ log }: { log: YieldLogEntry }) {
    const { t } = useTranslation();
    const d = log.details ?? {};

    const rendues = d.nights_restored ?? log.dates_count;
    const interrupteur = restoreSwitchNote(d);
    const motifs: string[] = [];

    if (d.skipped_past) {
        motifs.push(`${d.skipped_past} ${t(NS, 'logs_skipped_past', 'passées')}`);
    }
    if (d.skipped_beyond_window) {
        motifs.push(`${d.skipped_beyond_window} ${t(NS, 'logs_skipped_beyond', 'au-delà de la fenêtre du yield (non touchées)')}`);
    }
    if (d.skipped_zero_price) {
        motifs.push(`${d.skipped_zero_price} ${t(NS, 'logs_skipped_zero', 'à prix nul')}`);
    }
    if (d.skipped_invalid) {
        motifs.push(`${d.skipped_invalid} ${t(NS, 'logs_skipped_invalid', 'sans date')}`);
    }

    return (
        <>
            {interrupteur === 'switched_off' && (
                <div className="text-muted-foreground text-xs">{t(NS, 'logs_yield_disabled', 'Yield éteint avant la restauration')}</div>
            )}
            {interrupteur === 'already_off' && (
                <div className="text-muted-foreground text-xs">{t(NS, 'logs_yield_already_off', 'Yield déjà éteint avant la restauration')}</div>
            )}
            <div className="text-muted-foreground text-xs">
                {rendues} {t(NS, 'logs_nights_restored', 'nuits rendues')}
                {d.currency ? ` ${d.currency}` : ''}
                {d.restore_until ? ` ${t(NS, 'logs_restore_until', 'jusqu’au')} ${formatDay(d.restore_until)}` : ''}
                {d.skipped ? (
                    <>
                        {' · '}
                        {d.skipped} {t(NS, 'logs_skipped', 'écartées')}
                        {motifs.length > 0 && ` (${motifs.join(', ')})`}
                    </>
                ) : null}
            </div>
            {/* Un compte de DATES de la fenêtre, pas de nuits de la photo : il
                recouvre les nuits « à prix nul » ci-dessus, et les dates que la
                photo ne couvrait pas du tout. Il ne s'ajoute pas aux écartées. */}
            {(d.nights_without_origin ?? 0) > 0 && (
                <div className="text-muted-foreground text-xs">
                    {d.nights_without_origin} {t(NS, 'logs_without_origin', 'sans prix d’origine (gardent le prix du yield)')}
                </div>
            )}
            {(d.backup_nights || d.backup_captured_at) && (
                <div className="text-muted-foreground text-xs">
                    {t(NS, 'logs_backup_of', 'Photo du')}{' '}
                    {d.backup_captured_at ? formatDay(d.backup_captured_at) : '—'}
                    {d.backup_nights ? ` · ${d.backup_nights} ${t(NS, 'logs_nights', 'nuits')}` : ''}
                </div>
            )}
            {d.backup_kept && (
                <div className="text-muted-foreground text-xs">
                    {t(NS, 'logs_backup_kept', 'Sauvegarde conservée : la restauration peut être relancée.')}
                </div>
            )}
            {d.sample && <SampleLine sample={d.sample} />}
        </>
    );
}

/** La photo : combien de nuits, sur quelle période, dans quelle devise. */
function BackupDetails({ log }: { log: YieldLogEntry }) {
    const { t } = useTranslation();
    const d = log.details ?? {};

    return (
        <>
            <div className="text-muted-foreground text-xs">
                {d.nights_captured ?? log.dates_count}{' '}
                {t(NS, 'logs_nights_captured', 'nuits photographiées')}
                {d.currency ? ` ${d.currency}` : ''}
                {d.from && d.to ? ` · ${formatDay(d.from)} → ${formatDay(d.to)}` : ''}
                {d.empty_origin ? ` · ${t(NS, 'logs_empty_origin', 'aucun prix d’origine')}` : ''}
            </div>
            {d.sample && <SampleLine sample={d.sample} />}
        </>
    );
}

/**
 * Un push REFUSÉ : rien n'est parti. Le motif est affiché sous la ligne (champ
 * `error`) ; ici, ce qui manque au motif — l'absence d'envoi, le prochain
 * essai automatique, et la forme de la réponse illisible le cas échéant.
 */
function RefusedPushDetails({ log }: { log: YieldLogEntry }) {
    const { t } = useTranslation();
    const d = log.details ?? {};
    const prochainEssai = formatCapturedAt(d.retry_after ?? null);
    const empreinte = describeFingerprint(d.fingerprint);

    return (
        <>
            <div className="text-muted-foreground text-xs">
                {t(NS, 'logs_refused_nothing_sent', 'Aucune nuit envoyée')}
                {prochainEssai ? ` · ${t(NS, 'logs_retry_after', 'Nouvel essai automatique à partir du')} ${prochainEssai}` : ''}
            </div>
            {empreinte && (
                <div className="text-muted-foreground text-xs">
                    {t(NS, 'logs_fingerprint', 'Forme de la réponse')} : <span className="font-mono">{empreinte}</span>
                </div>
            )}
        </>
    );
}

/** Le push : ce qui est parti, et les nuits réservées écartées. */
function PushDetails({ log }: { log: YieldLogEntry }) {
    const { t } = useTranslation();
    const d = log.details ?? {};

    // AVANT la sortie anticipée ci-dessous : un refus a `dates_count = 0` et
    // pas de `booked_nights_skipped`, il n'afficherait donc jamais rien.
    if (isRefusedLog(d)) {
        return <RefusedPushDetails log={log} />;
    }

    if (log.dates_count === 0 && d.booked_nights_skipped === undefined) {
        return null;
    }

    return (
        <>
            <div className="text-muted-foreground text-xs">
                {log.dates_count} {t(NS, 'logs_nights_pushed', 'nuits traitées')}
                {d.from && d.to ? ` · ${formatDay(d.from)} → ${formatDay(d.to)}` : ''}
                {typeof d.avg_price === 'number' && (
                    <>
                        {' · '}avg: {d.avg_price}
                        {typeof d.min_price === 'number' && ` · min: ${d.min_price}`}
                        {typeof d.max_price === 'number' && ` · max: ${d.max_price}`}
                    </>
                )}
                {d.booked_nights_skipped ? (
                    <>
                        {' · '}
                        {d.booked_nights_skipped}{' '}
                        {t(NS, 'logs_booked_skipped', 'réservées ignorées')}
                    </>
                ) : null}
            </div>
            {d.sample && <SampleLine sample={d.sample} />}
        </>
    );
}

function LogDetails({ log }: { log: YieldLogEntry }) {
    if (log.action === 'restore') {
        return <RestoreDetails log={log} />;
    }
    if (log.action === 'backup') {
        return <BackupDetails log={log} />;
    }

    return <PushDetails log={log} />;
}

export default function YieldLogs() {
    const { logs } = usePage<PageProps>().props;
    const { t, locale } = useTranslation();
    // Le périmètre affiché (`?coHost=`) suit le retour au tableau ; les liens
    // de page le portent déjà (paginateur `withQueryString()` côté serveur).
    const scope = scopeQuery(useYieldScope().coHost);

    const breadcrumbs: BreadcrumbItem[] = [
        { title: t(NS, 'breadcrumb_yield', 'Yield Manager'), href: route('yield.index', scope) },
        { title: t(NS, 'history', 'Historique'), href: route('yield.logs', scope) },
    ];

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title={t(NS, 'logs_head_title', 'Yield — Historique')} />
            <div className="flex flex-col gap-6 p-6">
                <div className="flex flex-wrap items-center gap-4">
                    <Button variant="ghost" size="icon" onClick={() => router.visit(route('yield.index', scope))}>
                        <ArrowLeft className="h-4 w-4" />
                    </Button>
                    <div>
                        <h1 className="text-xl font-bold">
                            {t(NS, 'logs_title', 'Historique du Yield Manager')}
                        </h1>
                        <p className="text-muted-foreground text-sm">
                            {logs.total} {t(NS, 'logs_entries', 'entrées')}
                        </p>
                    </div>
                    <div className="ml-auto">
                        <FranchiseSelect routeName="yield.logs" />
                    </div>
                </div>

                <FranchiseReadOnlyBanner />

                <Card>
                    <CardContent className="p-0">
                        <div className="divide-y">
                            {logs.data.length === 0 ? (
                                <div className="text-muted-foreground p-8 text-center">
                                    {t(NS, 'logs_empty', 'Aucun log pour le moment')}
                                </div>
                            ) : (
                                logs.data.map((log) => (
                                    <div key={log.id} className="flex items-start gap-4 p-4">
                                        <div className="flex-1 space-y-1">
                                            <div className="flex items-center gap-2">
                                                <ActionBadge action={log.action} />
                                                <StatusBadge status={log.status} details={log.details} />
                                                {log.property_id && (
                                                    <span className="text-muted-foreground text-xs">
                                                        {t(NS, 'logs_property', 'Logement')}: {log.property_id}
                                                        {log.yield_config?.internal_id &&
                                                            ` (#${log.yield_config.internal_id})`}
                                                    </span>
                                                )}
                                            </div>
                                            <LogDetails log={log} />
                                            {log.error && (
                                                <div className="text-destructive text-xs">{log.error}</div>
                                            )}
                                        </div>
                                        <div className="text-muted-foreground text-xs whitespace-nowrap">
                                            {/* La page vient d'être traduite : la DATE doit suivre la locale, sinon
                                                un utilisateur en anglais lit des libellés anglais sur des dates françaises. */}
                                            {new Date(log.created_at).toLocaleString(locale, {
                                                day: '2-digit',
                                                month: '2-digit',
                                                hour: '2-digit',
                                                minute: '2-digit',
                                            })}
                                        </div>
                                    </div>
                                ))
                            )}
                        </div>
                    </CardContent>
                </Card>

                {/* Pagination */}
                {logs.last_page > 1 && (
                    <div className="flex items-center justify-center gap-2">
                        <Button
                            variant="outline"
                            size="sm"
                            disabled={!logs.prev_page_url}
                            onClick={() => logs.prev_page_url && router.visit(logs.prev_page_url)}
                        >
                            <ChevronLeft className="h-4 w-4" />
                        </Button>
                        <span className="text-muted-foreground text-sm">
                            {t(NS, 'logs_page', 'Page')} {logs.current_page} / {logs.last_page}
                        </span>
                        <Button
                            variant="outline"
                            size="sm"
                            disabled={!logs.next_page_url}
                            onClick={() => logs.next_page_url && router.visit(logs.next_page_url)}
                        >
                            <ChevronRight className="h-4 w-4" />
                        </Button>
                    </div>
                )}
            </div>
        </AppLayout>
    );
}
