@inject ServiceLivresApi Api
@inject EtatReseau Reseau
@implements IDisposable

@*
    Prêts d'un livre : l'état courant, l'action qui s'impose, puis l'historique.

    Mobile d'abord : une seule colonne, un seul bouton visible à la fois. Prêter et rendre sont
    les deux gestes du quotidien — ils sont en haut, l'historique se déroule dessous.
*@
<section class="bloc-prets">
    <h2 class="titre-section">Prêt</h2>

    @if (Format == MaBibli.Shared.Entites.Format.Numerique)
    {
        @* CLAUDE.md : un ebook n'est qu'une fiche, il n'y a pas d'exemplaire à confier.
           L'API le refuse aussi — ici on évite simplement de proposer un geste impossible. *@
        <p class="message-discret">
            Un livre numérique ne se prête pas : il n'y a pas d'exemplaire à confier.
        </p>
    }
    else if (!Reseau.EnLigne)
    {
        @*
            L'historique des prêts n'est pas mis en cache : une requête par livre pour une
            information rarement consultée. En revanche l'état COURANT vient de la fiche, donc
            de l'instantané du catalogue — c'est lui qui répond à « où est ce livre ? », la
            seule question qui se pose vraiment sans réseau, devant l'étagère.

            Afficher une liste vide ferait croire à « jamais prêté » : on dit ce qu'on sait, et
            on dit ce qu'on ignore.
        *@
        @if (PreteA is { } absent)
        {
            <p class="bandeau-pret">
                <span class="etiquette etiquette-prete">Prêté</span>
                à <strong>@absent</strong>
                @if (PreteDepuis is { } depuis)
                {
                    @($" depuis le {Jour(depuis)}")
                }
            </p>
        }
        else
        {
            <p class="message-discret">Ce livre était à la maison à la dernière synchronisation.</p>
        }

        <p class="message-discret">
            Hors ligne : l'historique des prêts n'est pas consultable, et prêter ou marquer un
            retour demande le réseau.
        </p>
    }
    else if (_prets is null)
    {
        <p class="message-discret">Chargement…</p>
    }
    else
    {
        @if (_erreur is not null)
        {
            <p class="message-erreur" role="alert">@_erreur</p>
        }

        @if (EnCours is { } pret)
        {
            <p class="bandeau-pret">
                <span class="etiquette etiquette-prete">Prêté</span>
                à <strong>@pret.Emprunteur</strong>
                depuis le @Jour(pret.DatePret)
                <span class="pret-duree">(@Duree(pret.JoursEcoules))</span>
            </p>

            <div class="actions-formulaire">
                <button type="button" class="bouton bouton-principal" disabled="@_occupe"
                        @onclick="RendreAsync">
                    @(_occupe ? "Enregistrement…" : "Marquer comme rendu")
                </button>
            </div>
        }
        else if (!_formulaireOuvert)
        {
            <p class="message-discret">Ce livre est à la maison.</p>

            <div class="actions-formulaire">
                <button type="button" class="bouton bouton-principal" @onclick="OuvrirFormulaire">
                    Prêter ce livre
                </button>
            </div>
        }
        else
        {
            @* Emprunteur en texte libre : ce sont des gens du dehors, sans compte YunoHost. *@
            <label class="champ">
                <span class="champ-libelle">Prêté à <span class="obligatoire" aria-hidden="true">*</span></span>
                <input class="champ-saisie" type="text" @bind="_emprunteur" @bind:event="oninput"
                       @onkeydown="SurTouche" placeholder="Prénom, nom…" autocomplete="off" />
            </label>

            <label class="champ">
                <span class="champ-libelle">
                    Depuis le<span class="champ-aide"> (on note souvent un prêt après coup)</span>
                </span>
                <input class="champ-saisie" type="date" @bind="_datePret"
                       max="@DateTime.Today.ToString("yyyy-MM-dd")" />
            </label>

            <div class="actions-formulaire">
                <button type="button" class="bouton bouton-principal"
                        disabled="@(_occupe || string.IsNullOrWhiteSpace(_emprunteur))"
                        @onclick="PreterAsync">
                    @(_occupe ? "Enregistrement…" : "Prêter")
                </button>
                <button type="button" class="bouton bouton-discret" disabled="@_occupe"
                        @onclick="() => _formulaireOuvert = false">
                    Annuler
                </button>
            </div>
        }

        @if (Historique.Count > 0)
        {
            @* L'historique est la raison d'être de la table : un prêt rendu ne disparaît pas. *@
            <h3 class="titre-sous-section">Prêts passés</h3>

            <ul class="liste-historique">
                @foreach (var passe in Historique)
                {
                    <li @key="passe.Id">
                        <strong>@passe.Emprunteur</strong>
                        <span class="historique-dates">
                            du @Jour(passe.DatePret) au @Jour(passe.DateRetour!.Value)
                            — @Duree(passe.JoursEcoules)
                        </span>
                    </li>
                }
            </ul>
        }
        else if (EnCours is null)
        {
            <p class="message-discret">Jamais prêté.</p>
        }
    }
</section>

@code {
    [Parameter, EditorRequired] public int LivreId { get; set; }

    /// <summary>Format du livre : un ebook ne se prête pas.</summary>
    [Parameter] public Format Format { get; set; }

    /// <summary>
    /// Emprunteur actuel d'après la fiche. Sert <b>hors-ligne</b>, où l'historique n'est pas
    /// consultable mais où l'état courant, lui, vient de l'instantané du catalogue.
    /// </summary>
    [Parameter] public string? PreteA { get; set; }

    /// <summary>Date du prêt en cours, même origine que <see cref="PreteA"/>.</summary>
    [Parameter] public DateTime? PreteDepuis { get; set; }

    /// <summary>Prévient la page parente qu'un prêt a changé, pour qu'elle rafraîchisse sa fiche.</summary>
    [Parameter] public EventCallback OnChangement { get; set; }

    private IReadOnlyList<PretDto>? _prets;
    private bool _formulaireOuvert;
    private bool _occupe;
    private string _emprunteur = string.Empty;
    private DateTime _datePret = DateTime.Today;
    private string? _erreur;
    private int _livreCharge;

    private PretDto? EnCours => _prets?.FirstOrDefault(p => p.EstEnCours);

    private IReadOnlyList<PretDto> Historique =>
        _prets?.Where(p => !p.EstEnCours).ToList() ?? [];

    protected override void OnInitialized() => Reseau.Change += SurChangementReseau;

    /// <summary>Au retour du réseau, l'historique devient consultable : on va le chercher.</summary>
    private void SurChangementReseau() => _ = InvokeAsync(async () =>
    {
        if (Reseau.EnLigne)
        {
            await ChargerAsync();
        }

        StateHasChanged();
    });

    public void Dispose() => Reseau.Change -= SurChangementReseau;

    protected override async Task OnParametersSetAsync()
    {
        if (_livreCharge != LivreId)
        {
            _livreCharge = LivreId;
            _prets = null;
            await ChargerAsync();
        }
    }

    private async Task ChargerAsync()
    {
        if (!Reseau.EnLigne)
        {
            return; // L'historique n'est pas en cache : rien à demander, l'affichage le dit.
        }

        try
        {
            _prets = await Api.ListerPretsLivreAsync(LivreId) ?? [];
        }
        catch (Exception ex)
        {
            _erreur = $"Impossible de charger les prêts : {ex.Message}";
            _prets = [];
        }
    }

    private void OuvrirFormulaire()
    {
        _emprunteur = string.Empty;
        _datePret = DateTime.Today;
        _erreur = null;
        _formulaireOuvert = true;
    }

    private async Task SurTouche(KeyboardEventArgs e)
    {
        if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(_emprunteur))
        {
            await PreterAsync();
        }
    }

    private async Task PreterAsync()
    {
        _occupe = true;
        _erreur = null;

        // La date n'est transmise que si elle diffère d'aujourd'hui : sinon on laisse le serveur
        // horodater à l'instant présent, plus précis que « minuit ce matin ».
        var date = _datePret.Date == DateTime.Today ? (DateTime?)null : _datePret;
        var resultat = await Api.PreterAsync(LivreId, _emprunteur.Trim(), date);

        _occupe = false;

        if (!resultat.EstOk)
        {
            _erreur = resultat.Erreur;
            return;
        }

        _formulaireOuvert = false;
        await ChargerAsync();
        await OnChangement.InvokeAsync();
    }

    private async Task RendreAsync()
    {
        _occupe = true;
        _erreur = null;

        var resultat = await Api.RendreAsync(EnCours!.Id);

        _occupe = false;

        if (!resultat.EstOk)
        {
            _erreur = resultat.Erreur;

            // Le prêt a peut-être été clos ailleurs (autre membre du foyer, autre onglet) :
            // recharger vaut mieux que laisser un bouton qui ne peut plus rien faire.
            await ChargerAsync();
            return;
        }

        await ChargerAsync();
        await OnChangement.InvokeAsync();
    }

    private static string Jour(DateTime instant) =>
        instant.ToLocalTime().ToString("dd/MM/yyyy");

    /// <summary>Durée en clair : « 3 jours », « 2 mois ». Le nombre de jours exact ne parle plus au-delà.</summary>
    internal static string Duree(int jours) => jours switch
    {
        0 => "aujourd'hui",
        1 => "1 jour",
        < 31 => $"{jours} jours",
        < 365 => $"{jours / 30} mois",
        < 730 => "plus d'un an",
        _ => $"plus de {jours / 365} ans",
    };
}
