import { useTranslation } from '@/hooks/use-translation';
import { AlertTriangle, FileDown, PencilLine } from 'lucide-react';
import { dateHeureCourteFr, lignesRefusees, motifDuRefus } from './depot';
import { moisDeLaLigne } from './format';
import type { RetenueLigne } from './types';

/**
 * « Ces lignes ont été REFUSÉES par la DGI » — le bandeau qui n'existait pas.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * DIX-SEPT FICHIERS REFUSÉS, ET UN ÉCRAN QUI AFFICHAIT « DÉPOSÉ »
 * ═════════════════════════════════════════════════════════════════════════════
 * Le manager n'interrogeait que l'écran des CERTIFICATS de la DGI. Or un refus
 * ne s'y voit pas : il s'y présente exactement comme une analyse en cours, par
 * une ABSENCE. Le verdict, lui, vit sur l'écran des DÉPÔTS. Résultat : la ligne
 * affichait « déposé », son bouton d'envoi avait disparu, et la retenue
 * n'aurait jamais été déclarée — sans qu'aucune surface de l'application ne
 * soit capable de le dire.
 *
 * Il a la forme de `fiscal-warning.tsx` — les deux se lisent dans le même
 * geste, l'un avant l'envoi, l'autre après — mais plus sa couleur. Il était
 * amber comme lui ; il est ROUGE depuis que la ligne dit « Refusé par la DGI »
 * en rouge et « En attente DGI » en amber. Un bandeau de refus amber, posé
 * au-dessus de lignes en attente amber, se lisait comme une attente de plus.
 * Ce qui serait refusé (le pré-vol) reste amber ; ce qui l'A ÉTÉ est rouge,
 * comme sur le portail de la DGI.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * DEUX ACTIONS, ET SEULEMENT DEUX. AUCUN BOUTON « REDÉPOSER ».
 * ═════════════════════════════════════════════════════════════════════════════
 * Un second dépôt identique n'est pas une correction : c'est une SECONDE
 * DÉCLARATION, et la plateforme ne refuse pas les doublons. Le dépôt reste où
 * il a toujours été — dans le menu de la ligne, derrière l'aperçu qu'on lit
 * avant d'envoyer.
 *
 * Ce que ce bandeau propose, donc :
 *   le RAPPORT de la DGI ... servi depuis NOTRE archive, jamais redemandé à la
 *       DGI au clic : sa bannière dit « Veuillez télécharger vos rapports
 *       d'erreurs avant minuit, car ils seront supprimés ». Un lien qui irait
 *       le rechercher serait cassé la moitié du temps ;
 *   CORRIGER LA LIGNE ...... qui rouvre le formulaire sur place. Ce n'est plus
 *       une impasse polie : le formulaire dit lui-même, AVANT la saisie, si la
 *       ligne est encore figée et ce qui la rouvrira — la relecture de l'état
 *       DGI, que l'écran lance lui-même, pas une tâche de nuit qui ne tournait
 *       pas là où l'on envoie.
 *
 * Et le MOTIF, sous chaque ligne : le texte de la DGI extrait de son rapport
 * archivé. Il dit QUOI corriger sans avoir à ouvrir le rapport — et quand il
 * manque, on ne le devine pas : le lien vers le rapport reste la référence.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * LE FILTRE MASQUE DES LIGNES, LE COMPTEUR NON — ET ON LE DIT
 * ═════════════════════════════════════════════════════════════════════════════
 * Les listes de lignes sont FILTRÉES côté serveur ; les compteurs de la bande
 * de statut, eux, sont calculés sur toute la déclaration. Un bandeau bâti sur
 * les lignes visibles peut donc en montrer moins que le compteur n'en annonce.
 * Plutôt que de laisser deviner l'écart, `totalRefuses` le nomme.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * LES ENVOIS JAMAIS REÇUS Y FIGURENT AUSSI — SANS ÊTRE DITS « REFUSÉS »
 * ═════════════════════════════════════════════════════════════════════════════
 * Une ligne envoyée que la DGI ne liste toujours pas, plus de dix minutes après
 * et sur une lecture complète, est à RENVOYER (`non_recu`) — et elle est
 * comptée dans le même compteur `a_refaire`. L'exclure d'ici ferait annoncer
 * « masquée par le filtre » une ligne pourtant affichée. Elle est donc nommée,
 * sans motif ni rapport (il n'y en a pas : la DGI n'a rien vu), et le titre
 * cesse de parler de refus dès qu'il en contient une.
 *
 * Le lien du RAPPORT exige la permission du dépôt (`peutLireLeRapport`) : la
 * route la porte, et un lien vers un 403 serait un piège.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * SUR PLUSIEURS MOIS, CHAQUE REFUS DIT SON MOIS
 * ═════════════════════════════════════════════════════════════════════════════
 * La ligne du propriétaire d'un logement existe une fois PAR MOIS. Refusée en
 * janvier et en mars, elle s'affichait deux fois « Dupont — Villa Azur », sans
 * rien pour dire quel refus — quel rapport, quelle correction — appartient à
 * quelle déclaration. `moisParDeclaration` (plage seulement, comme pour
 * `FiscalWarning`) écrit le mois à côté du nom.
 */
export function DepotRefuse({
    lignes,
    totalRefuses,
    onCorrigerLigne,
    peutLireLeRapport = false,
    moisParDeclaration = null,
}: {
    /** Les lignes AFFICHÉES — filtrées, donc potentiellement incomplètes. */
    lignes: RetenueLigne[];
    /** Compteur non filtré de la déclaration, pour nommer ce que le filtre cache. */
    totalRefuses?: number;
    /** Ouvre le formulaire de la ligne sur place. Absent ⇒ pas de bouton mort. */
    onCorrigerLigne?: (ligneId: number) => void;
    /** `can.declare` — la permission EXACTE de la route du rapport. */
    peutLireLeRapport?: boolean;
    /** `declaration_id` → « Mars 2026 ». Plage seulement. */
    moisParDeclaration?: Record<number, string> | null;
}) {
    const { t } = useTranslation();

    // La règle 1 (« un certificat annulé l'emporte sur l'état du dépôt ») n'est
    // pas réécrite ici : elle vit dans `depot.ts` et y est testée. Deux copies
    // finiraient par diverger, et la divergence dirait à l'utilisateur de
    // redéposer une retenue qui n'existe plus chez la DGI.
    const refusees = lignesRefusees(lignes);

    if (refusees.length === 0) return null;

    // Ce que le filtre en cours retire à ce bandeau, et que le compteur compte.
    const masquees = Math.max(0, (totalRefuses ?? refusees.length) - refusees.length);
    // Un envoi jamais reçu n'est pas un refus : le titre ne le dit pas refusé.
    const avecNonRecus = refusees.some((ligne) => ligne.tej_depot?.non_recu === true);

    return (
        <div className="flex flex-col gap-2 rounded-md border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-900">
            <div className="flex items-center gap-2 font-medium">
                <AlertTriangle className="h-4 w-4 shrink-0" />
                {avecNonRecus
                    ? t('retenue', 'depot_a_renvoyer', ':count ligne(s) à renvoyer : refusée(s) par la DGI ou jamais reçue(s)').replace(
                          ':count',
                          String(refusees.length),
                      )
                    : refusees.length === 1
                      ? t('retenue', 'depot_refuses_un', '1 ligne a été refusée par la DGI')
                      : t('retenue', 'depot_refuses_plusieurs', ':count lignes ont été refusées par la DGI').replace(
                            ':count',
                            String(refusees.length),
                        )}
            </div>
            <p className="text-xs text-red-800">
                {t(
                    'retenue',
                    'depot_refuses_aide',
                    'La DGI n’a pas accepté le fichier envoyé : ces retenues ne sont pas déclarées. Corrigez ce que dit le motif, puis renvoyez la ligne depuis son menu. Le manager relit l’état DGI de lui-même à l’ouverture de l’écran tant qu’un envoi attend son verdict, et à la demande avec « Actualiser l’état DGI ».',
                )}
            </p>

            <ul className="flex flex-col gap-1.5 pt-1">
                {refusees.map((ligne) => {
                    const motif = motifDuRefus(ligne);
                    const nonRecue = ligne.tej_depot?.non_recu === true;
                    const mois = moisDeLaLigne(moisParDeclaration, ligne.declaration_id);

                    return (
                        <li key={ligne.id} className="flex flex-col gap-0.5 rounded border border-red-200 bg-white/60 px-2.5 py-1.5">
                            <span className="text-xs font-medium">
                                {ligne.beneficiaire_nom}
                                {ligne.property_nom && <span className="font-normal text-red-700"> — {ligne.property_nom}</span>}
                                {mois && <span className="font-normal text-red-700"> · {mois}</span>}
                            </span>

                            {/* LE MOTIF, dans le texte de la DGI : c'est lui qu'on
                                corrige. Deux lignes au plus, le reste au survol —
                                un motif de rapport peut tenir un paragraphe. */}
                            {motif && (
                                <span className="line-clamp-2 text-xs text-red-900" title={motif}>
                                    {t('retenue', 'depot_motif', 'Motif : :motif').replace(':motif', () => motif)}
                                </span>
                            )}

                            {/* Des FAITS, dans les mots de la DGI : son numéro de
                                dépôt (le seul qu'un humain puisse retaper sur son
                                portail), l'heure exacte de l'envoi, et son code
                                brut. */}
                            <span className="text-xs text-red-800">
                                {nonRecue
                                    ? t('retenue', 'depot_non_recu_bandeau', 'Jamais reçue par la DGI — envoyée le :date').replace(
                                          ':date',
                                          dateHeureCourteFr(ligne.tej_depot?.envoye_at),
                                      )
                                    : faitsDuRefus(ligne, t)}
                            </span>

                            <div className="flex flex-wrap items-center gap-x-4 gap-y-1 pt-0.5">
                                {nonRecue ? null : peutLireLeRapport && ligne.tej_depot?.rapport_url ? (
                                    <button
                                        type="button"
                                        /* Téléchargement : surtout pas une visite
                                           Inertia, qui n'attend pas un fichier. */
                                        onClick={() => window.open(ligne.tej_depot?.rapport_url ?? '', '_blank')}
                                        className="flex w-fit items-center gap-1 text-xs font-medium underline underline-offset-2 hover:text-red-950"
                                    >
                                        <FileDown className="h-3 w-3" />
                                        {t('retenue', 'depot_rapport', 'Télécharger le rapport de la DGI')}
                                    </button>
                                ) : (
                                    /* Le rapport meurt à minuit chez la DGI. Son
                                       absence n'est pas une panne du manager : on
                                       le dit plutôt que d'afficher un lien mort. */
                                    <span className="text-xs text-red-700">
                                        {t('retenue', 'depot_rapport_absent', 'Rapport indisponible — la DGI l’efface chaque nuit')}
                                    </span>
                                )}

                                {onCorrigerLigne && (
                                    <button
                                        type="button"
                                        onClick={() => onCorrigerLigne(ligne.id)}
                                        className="flex w-fit items-center gap-1 text-xs font-medium underline underline-offset-2 hover:text-red-950"
                                    >
                                        <PencilLine className="h-3 w-3" />
                                        {t('retenue', 'depot_corriger', 'Corriger la ligne')}
                                    </button>
                                )}
                            </div>
                        </li>
                    );
                })}
            </ul>

            {masquees > 0 && (
                <p className="text-xs text-red-800">
                    {t(
                        'retenue',
                        'depot_refuses_masques',
                        ':count ligne(s) refusée(s) ne sont pas affichées ici : le filtre en cours les masque.',
                    ).replace(':count', String(masquees))}
                </p>
            )}
        </div>
    );
}

/**
 * « Dépôt n° IN260261 · Déposé le 05/08 à 23:41 · NOT_VALID (Non valide) ».
 *
 * Un champ absent ne produit pas son fragment — « Dépôt n° null » serait pire
 * que le silence, l'écran aurait l'air de savoir. Même doctrine que
 * `lignesInfobulle` dans `depot.ts`.
 */
function faitsDuRefus(ligne: RetenueLigne, t: (namespace: string, key: string, fallback?: string) => string): string {
    const depot = ligne.tej_depot;

    if (!depot) return '';

    const fragments: string[] = [];

    if (depot.numero) {
        fragments.push(t('retenue', 'depot_numero', 'Dépôt n° :numero').replace(':numero', depot.numero));
    }

    const depose = dateHeureCourteFr(depot.depose_at);
    if (depose !== '') {
        fragments.push(t('retenue', 'depot_depose_le', 'Déposé le :date').replace(':date', depose));
    }

    if (depot.code) {
        fragments.push(depot.libelle_dgi ? `${depot.code} (${depot.libelle_dgi})` : depot.code);
    }

    return fragments.join(' · ');
}
