@page "/Configuracion"
@rendermode InteractiveServer
@attribute [Authorize]

@using System.Globalization
@using GestionaDenunciasAN.Services
@using Microsoft.AspNetCore.Components.Authorization

@inject ApiDenunciasClient ApiDenuncias
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject UiBusyService Busy

<PageTitle>Configuracion</PageTitle>

<h3>Configuracion</h3>

@if (!hasCheckedConfigurationAccess)
{
    <div class="card mt-3">
        <div class="card-body">
            <span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
            Cargando configuracion...
        </div>
    </div>
}
else if (!hasConfigurationAccess)
{
    <div class="alert alert-warning mt-3">
        <strong>Configuracion restringida.</strong>
        <div>Esta pantalla solo esta disponible para los usuarios autorizados del sistema.</div>
    </div>
}
else
{
<div class="card mt-3">
    <div class="card-body">
        <h5 class="mb-3">Fecha de actualizaciones externas</h5>

        <div class="row g-3 align-items-start">
            <div class="col-12 col-lg-4">
                <label class="form-label" for="external-update-cutoff">Fecha de corte</label>
                <input id="external-update-cutoff"
                       class="form-control"
                       type="text"
                       inputmode="numeric"
                       maxlength="10"
                       placeholder="dd/mm/aaaa"
                       @bind="externalUpdateCutoffDate"
                       @bind:event="oninput"
                       disabled="@isSavingConfiguration" />
                <div class="form-text">Formato: dd/mm/aaaa. Ejemplo: 05/06/2026.</div>
            </div>

            <div class="col-12 col-lg-auto pt-lg-4">
                <div class="d-flex flex-wrap gap-2">
                    <button type="button"
                            class="btn btn-primary"
                            disabled="@isSavingConfiguration"
                            @onclick="SaveExternalUpdateCutoffDateAsync">
                        @if (isSavingConfiguration)
                        {
                            <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
                            <span class="ms-2">Guardando</span>
                        }
                        else
                        {
                            <span class="bi bi-floppy" aria-hidden="true"></span>
                            <span class="ms-1">Guardar fecha</span>
                        }
                    </button>

                    <button type="button"
                            class="btn btn-outline-secondary"
                            disabled="@isSavingConfiguration || string.IsNullOrWhiteSpace(externalUpdateCutoffDate)"
                            @onclick="ClearExternalUpdateCutoffDateAsync">
                        Limpiar
                    </button>
                </div>
            </div>
        </div>

        <div class="alert alert-info mt-3 mb-0">
            La comparacion por hash se mantiene igual. Esta fecha solo se usa cuando la actualizacion pertenece a un expediente que ya estaba en Gestiona y no tenemos hashes previos en esta BBDD: en ese caso se propondran como nuevos solo los adjuntos posteriores a la fecha de corte.
        </div>

        @if (!string.IsNullOrWhiteSpace(configurationNotice))
        {
            <div class="alert alert-success mt-3 mb-0">@configurationNotice</div>
        }

        @if (!string.IsNullOrWhiteSpace(configurationError))
        {
            <div class="alert alert-danger mt-3 mb-0">@configurationError</div>
        }
    </div>
</div>

<div class="card mt-3">
    <div class="card-body">
        <h5 class="mb-3">Purga manual con reemplazo</h5>

        <div class="border rounded px-3 py-2 bg-light mb-3">
            <div class="d-flex flex-wrap align-items-center gap-2">
                <span class="bi bi-key-fill text-primary" aria-hidden="true"></span>
                <strong>Ultima clave diaria registrada</strong>
                <span class="text-muted">@(string.IsNullOrWhiteSpace(latestEncryptionKeyDate) ? "No disponible" : latestEncryptionKeyDate)</span>
                @if (!string.IsNullOrWhiteSpace(latestEncryptionKeyStatus))
                {
                    <span class="badge rounded-pill text-bg-secondary">@latestEncryptionKeyStatus</span>
                }
            </div>
            <div class="form-text mt-1">
                Es la fecha de la fila mas reciente en <code>encryption_keys</code>; sirve para comprobar rapidamente que la rotacion diaria esta llegando a la base de datos.
            </div>
        </div>

        <div class="alert alert-danger">
            <div class="d-flex gap-3">
                <span class="bi bi-exclamation-triangle-fill fs-3" aria-hidden="true"></span>
                <div>
                    <strong>Operacion critica e irreversible.</strong>
                    <div>
                        Esta accion llama a la Function App de claves para purgar criptograficamente la clave diaria actual y crear una clave nueva inmediatamente.
                        Las denuncias y adjuntos cifrados antes de la purga dejaran de poder abrirse.
                    </div>
                </div>
            </div>
        </div>

        <div class="row g-3">
            <div class="col-12">
                <label class="form-label">Alcance</label>
                <div class="border rounded px-3 py-2 bg-light">
                    <span class="bi bi-key me-2" aria-hidden="true"></span>
                    <strong>Clave diaria actual</strong>
                </div>
            </div>

            <div class="col-12 col-lg-7">
                <label class="form-label" for="purge-confirmation">Confirmacion escrita</label>
                <input id="purge-confirmation"
                       class="form-control"
                       placeholder="@RequiredConfirmation"
                       @bind="confirmation"
                       @bind:event="oninput"
                       disabled="@isExecutingPurge" />
                <div class="form-text">
                    Escribe exactamente: <code>@RequiredConfirmation</code>
                </div>
            </div>
        </div>

        <div class="form-check mt-3">
            <input id="purge-risk"
                   class="form-check-input"
                   type="checkbox"
                   @bind="acceptedRisk"
                   disabled="@isExecutingPurge" />
            <label class="form-check-label" for="purge-risk">
                Entiendo que esta purga dejara ilegibles las denuncias cifradas antes del reemplazo de clave.
            </label>
        </div>

        <div class="d-flex justify-content-start mt-3">
            <button type="button"
                    class="btn btn-danger"
                    disabled="@(!CanExecutePurge)"
                    @onclick="ExecutePurgeAsync">
                @if (isExecutingPurge)
                {
                    <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
                    <span class="ms-2">Ejecutando purga y reemplazo</span>
                }
                else
                {
                    <span class="bi bi-trash" aria-hidden="true"></span>
                    <span class="ms-1">Ejecutar purga y reemplazo</span>
                }
            </button>
        </div>

        <div class="alert alert-warning mt-3 mb-0">
            La API calculara la fecha actual de la clave diaria, obtendra la Function Key desde <code>KEYMGMT_FUNCTION_KEY</code> o desde el secreto <code>purge-function-key</code> de Key Vault y enviara la peticion a <code>manual_purge</code> con <code>replace: true</code>. Si la purga se completa pero falla la nueva clave, reintentara automaticamente con <code>force_rotate</code>.
        </div>
    </div>
</div>

@if (purgeResult is not null)
{
    <div class="alert alert-success mt-3">
        <strong>Purga con reemplazo solicitada correctamente para @purgeResult.Date.</strong>
        <div>Function App respondio con codigo @purgeResult.StatusCode.</div>
        @if (!string.IsNullOrWhiteSpace(purgeResult.ResponseBody))
        {
            <pre class="mt-2 mb-0 small">@purgeResult.ResponseBody</pre>
        }
    </div>
}

@if (!string.IsNullOrWhiteSpace(purgeErrorMessage))
{
    <div class="alert alert-danger mt-3">
        <strong>No se ha podido ejecutar la purga.</strong>
        <div>@purgeErrorMessage</div>
    </div>
}
}

@code {
    private const string RequiredConfirmation = "PURGAR CLAVE ACTUAL";
    private static readonly HashSet<string> ConfigurationUsers = new(StringComparer.OrdinalIgnoreCase)
    {
        "pcornejo",
        "Rgarciaglbk",
        "eaguilarGestor"
    };

    private bool hasCheckedConfigurationAccess;
    private bool hasConfigurationAccess;
    private string externalUpdateCutoffDate = string.Empty;
    private string latestEncryptionKeyDate = string.Empty;
    private string latestEncryptionKeyStatus = string.Empty;
    private string? configurationNotice;
    private string? configurationError;
    private bool isSavingConfiguration;

    private string confirmation = string.Empty;
    private bool acceptedRisk;
    private bool isExecutingPurge;
    private ManualPurgeResponse? purgeResult;
    private string? purgeErrorMessage;

    private bool CanExecutePurge =>
        acceptedRisk &&
        !isExecutingPurge &&
        string.Equals(confirmation.Trim(), RequiredConfirmation, StringComparison.Ordinal);

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        hasConfigurationAccess = IsConfigurationUser(authState.User.Identity?.Name);
        hasCheckedConfigurationAccess = true;
        if (!hasConfigurationAccess)
        {
            return;
        }

        await LoadConfigurationAsync();
    }

    private async Task LoadConfigurationAsync()
    {
        try
        {
            configurationError = null;
            var config = await ApiDenuncias.GetAppConfigurationAsync();
            externalUpdateCutoffDate = ToSpanishDateText(config.ExternalUpdateCutoffDate);
            latestEncryptionKeyDate = ToSpanishDateText(config.LatestEncryptionKeyDate);
            latestEncryptionKeyStatus = ToSpanishKeyStatus(config.LatestEncryptionKeyStatus);
        }
        catch (Exception ex)
        {
            configurationError = $"No se ha podido cargar la configuracion: {ex.Message}";
        }
    }

    private async Task SaveExternalUpdateCutoffDateAsync()
    {
        try
        {
            isSavingConfiguration = true;
            configurationNotice = null;
            configurationError = null;
            var date = ToIsoDateText(externalUpdateCutoffDate);

            var config = await ApiDenuncias.UpdateExternalUpdateCutoffDateAsync(date);
            externalUpdateCutoffDate = ToSpanishDateText(config.ExternalUpdateCutoffDate);
            latestEncryptionKeyDate = ToSpanishDateText(config.LatestEncryptionKeyDate);
            latestEncryptionKeyStatus = ToSpanishKeyStatus(config.LatestEncryptionKeyStatus);
            configurationNotice = string.IsNullOrWhiteSpace(externalUpdateCutoffDate)
                ? "Fecha de corte eliminada."
                : $"Fecha de corte guardada: {externalUpdateCutoffDate}.";
        }
        catch (Exception ex)
        {
            configurationError = $"No se ha podido guardar la fecha de corte: {ex.Message}";
        }
        finally
        {
            isSavingConfiguration = false;
        }
    }

    private async Task ClearExternalUpdateCutoffDateAsync()
    {
        externalUpdateCutoffDate = string.Empty;
        await SaveExternalUpdateCutoffDateAsync();
    }

    private static string? ToIsoDateText(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        var trimmed = value.Trim();
        if (DateOnly.TryParseExact(
            trimmed,
            "dd/MM/yyyy",
            CultureInfo.InvariantCulture,
            DateTimeStyles.None,
            out var spanishDate))
        {
            return spanishDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
        }

        if (DateOnly.TryParseExact(
            trimmed,
            "yyyy-MM-dd",
            CultureInfo.InvariantCulture,
            DateTimeStyles.None,
            out var isoDate))
        {
            return isoDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
        }

        throw new InvalidOperationException("La fecha de corte debe tener formato dd/mm/aaaa. Ejemplo: 05/06/2026.");
    }

    private static string ToSpanishDateText(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        return DateOnly.TryParseExact(
            value.Trim(),
            "yyyy-MM-dd",
            CultureInfo.InvariantCulture,
            DateTimeStyles.None,
            out var date)
            ? date.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture)
            : value.Trim();
    }

    private static string ToSpanishKeyStatus(string? status)
    {
        if (string.IsNullOrWhiteSpace(status))
        {
            return string.Empty;
        }

        return status.Trim().ToLowerInvariant() switch
        {
            "active" => "activa",
            "purged" => "purgada",
            _ => status.Trim()
        };
    }

    private static bool IsConfigurationUser(string? username)
        => !string.IsNullOrWhiteSpace(username) &&
           ConfigurationUsers.Contains(username.Trim());

    private async Task ExecutePurgeAsync()
    {
        if (!CanExecutePurge)
        {
            return;
        }

        isExecutingPurge = true;
        purgeResult = null;
        purgeErrorMessage = null;

        using var busy = Busy.Show(
            "Ejecutando purga manual",
            "Solicitando la purga criptografica y la creacion inmediata de una nueva clave diaria.");

        try
        {
            purgeResult = await ApiDenuncias.ExecuteCurrentManualPurgeAsync();
            await LoadConfigurationAsync();
            acceptedRisk = false;
            confirmation = string.Empty;
        }
        catch (Exception ex)
        {
            purgeErrorMessage = ex.Message;
        }
        finally
        {
            isExecutingPurge = false;
        }
    }
}
