﻿@page "/destinatario"
@using CaypeSeguimiento.Models
@using CaypeSeguimiento.Services
@using System.Net.Http.Headers
@using System.Net.Http.Json
@inject IAuthService AuthService
@inject NavigationManager Navigation
@inject IJSRuntime JS
@inject IHttpClientFactory HttpClientFactory

<PageTitle > Ca&amp;Pe — Destinatarios por Departamento</PageTitle >

<style >
.dept-combo-wrapper {
    position: relative;
}

.dept-input {
    width: 100%;
    height: 42px;
    padding: 0 36px 0 12px;
    border: 1.5px solid #dde3ef;
    border-radius: 8px;
    font-size: 0.875rem;
    color: #1e293b;
    background: #fff;
    outline: none;
    transition: border-color .2s, box-shadow .2s;
    box-sizing: border-box;
}

    .dept-input:focus {
        border-color: #0A1B4B;
        box-shadow: 0 0 0 3px rgba(10,27,75,.08);
    }

.dept-dropdown {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    background: #fff;
    border: 1.5px solid #dde3ef;
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(10,27,75,.13);
    max-height: 220px;
    overflow-y: auto;
    z-index: 999;
}

.dept-option {
    padding: 9px 14px;
    font-size: 0.85rem;
    color: #334155;
    cursor: pointer;
    transition: background .15s;
    display: flex;
    align-items: center;
    gap: 8px;
}

    .dept-option:hover,
    .dept-option.focused {
        background: #f0f4ff;
        color: #0A1B4B;
    }

    .dept-option.selected {
        background: #e8edf9;
        color: #0A1B4B;
        font-weight: 600;
    }

.dept-option-code {
    font-size: 0.72rem;
    color: #94a3b8;
    font-family: monospace;
    background: #f1f5f9;
    padding: 1px 5px;
    border-radius: 4px;
}

.dept-no-results {
    padding: 12px 14px;
    font-size: 0.83rem;
    color: #94a3b8;
    text-align: center;
}

.dept-clear-btn {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px;
    color: #94a3b8;
    display: flex;
    align-items: center;
    transition: color .15s;
}

    .dept-clear-btn:hover {
        color: #D40000;
    }

    .dept-clear-btn svg {
        width: 15px;
        height: 15px;
    }

.filter-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr auto;
    gap: 16px;
    align-items: end;
}

@media (max-width: 900px) {
    .filter-grid {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 580px) {
    .filter-grid {
        grid-template-columns: 1fr;
    }
}

.date-input {
    width: 100%;
    height: 42px;
    padding: 0 12px;
    border: 1.5px solid #dde3ef;
    border-radius: 8px;
    font-size: 0.875rem;
    color: #1e293b;
    background: #fff;
    outline: none;
    transition: border-color .2s, box-shadow .2s;
    box-sizing: border-box;
}

    .date-input:focus {
        border-color: #0A1B4B;
        box-shadow: 0 0 0 3px rgba(10,27,75,.08);
    }

</style >

<div class="page-wrapper" >
<!-- ===== NAVBAR ===== -->
<header class="navbar" >
<div class="navbar-brand" >
<div class="nav-logo-fallback" id="nav-logo-text-dest" >
<span class="nf-ca" > Ca</span > <span class="nf-amp" > &amp;</span > <span class="nf-pe" > Pe</span >
</div >
<img src="~/images/logo-caype.png" alt="Ca&amp;Pe" class="nav-logo" style="display:none"
onload="this.style.display='block'; document.getElementById('nav-logo-text-dest').style.display='none';"
onerror="this.style.display='none';" / >
</div >
<div class="navbar-right" >
@if (userData != null) {
    <div class="user-chip"> <div class="user-avatar">@GetInitials(userData.Cliente)</div> <div class="user-info"> <span class="user-name" title="@userData.Cliente">@userData.Cliente</span> <span class="user-code">Cód: @userData.CodCliente</span> </div> </div>
}

<button class="btn-logout" @onclick="Logout" title="Cerrar sesión" >
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" >
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" / >
<polyline points="16 17 21 12 16 7" / >
<line x1="21" y1="12" x2="9" y2="12" / >
</svg >
<span > Salir</span >
</button >
</div >
</header >
<!-- ===== CONTENIDO PRINCIPAL ===== -->
<main class="main-content" >

<div class="section-title-wrap" >
<div class="section-icon" >
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" >
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" / >
<circle cx="12" cy="10" r="3" / >
</svg >
</div >
<div >
<h1 class="section-title" > Destinatarios por Departamento</h1 >
<p class="section-subtitle" > Filtra por departamento y rango de fechas</p >
</div >
</div >
<!-- Tarjeta de filtros -->
<div class="search-card" >
<div class="search-card-body" >
<div class="filter-grid" >
<!-- Departamento (searchable) -->
<div >
<label class="input-label" >
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" >
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" / >
<circle cx="12" cy="10" r="3" / >
</svg >
Departamento
</label >
<div class="dept-combo-wrapper" @onfocusout="OnDeptFocusOut" >
<input type="text"
class="dept-input"
placeholder="Escribe o selecciona un departamento..."
value="@deptInputText"
@oninput="OnDeptInput"
@onfocus="OnDeptFocus"
@onkeydown="OnDeptKeyDown"
autocomplete="off" / >
@if (!string.IsNullOrEmpty(deptInputText)) {
    <button class="dept-clear-btn" @onclick="ClearDept" @onclick:stopPropagation="true" title="Limpiar"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <line x1="18" y1="6" x2="6" y2="18" /> <line x1="6" y1="6" x2="18" y2="18" /> </svg> </button>
}

@if (showDeptDropdown) {
    <div class="dept-dropdown" >
    @if (!DeptFiltered.Any()) {
        <div class="dept-no-results">Sin coincidencias</div>
    }

    else {
        @foreach (var (dep, idx) in DeptFiltered.Select((d, i) => (d, i))) {
            <div class="dept-option @(dep.Coddepartamento == selectedDeptCod ? "selected" : "") @(idx == deptFocusedIndex ? "focused" : "")" @onmousedown="() => SelectDept(dep)" @onmousedown:preventDefault="true"> <span class="dept-option-code">@dep.Coddepartamento</span> @dep.Departamento.Trim() </div>
        }
    }

    </div>
}

</div >
</div >
<!-- Fecha inicio -->
<div >
<label class="input-label" >
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" >
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" / >
<line x1="16" y1="2" x2="16" y2="6" / >
<line x1="8" y1="2" x2="8" y2="6" / >
<line x1="3" y1="10" x2="21" y2="10" / >
</svg >
Fecha inicio
</label >
<input type="date" class="date-input" @bind="fecInicio" / >
</div >
<!-- Fecha fin -->
<div >
<label class="input-label" >
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" >
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" / >
<line x1="16" y1="2" x2="16" y2="6" / >
<line x1="8" y1="2" x2="8" y2="6" / >
<line x1="3" y1="10" x2="21" y2="10" / >
</svg >
Fecha fin
</label >
<input type="date" class="date-input" @bind="fecFin" / >
</div >
<!-- Botón buscar -->
<button class="btn-buscar"
@onclick="Buscar"
disabled="@(isSearching || string.IsNullOrWhiteSpace(selectedDeptCod))" >
@if (isSearching) {
    <span class="btn-spinner"></span> <span>Buscando...</span>
}

else {
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="11" cy="11" r="8" /> <line x1="21" y1="21" x2="16.65" y2="16.65" /> </svg> <span>Buscar</span>
}

</button >
</div >
</div >
</div >
<!-- Estados error -->
@if (!string.IsNullOrEmpty(errorMessage)) {
    <div class="state-card error-card"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="10" /> <line x1="12" y1="8" x2="12" y2="12" /> <line x1="12" y1="16" x2="12.01" y2="16" /> </svg> <p>@errorMessage</p> </div>
}

<!-- Sin resultados -->
@if (hasBuscado && !isSearching && (resultados == null || !resultados.Any()) && string.IsNullOrEmpty(errorMessage)) {
    <div class="state-card empty-card"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="11" cy="11" r="8" /> <line x1="21" y1="21" x2="16.65" y2="16.65" /> <line x1="8" y1="11" x2="14" y2="11" /> </svg> <div> <p class="empty-title">Sin resultados</p> <p class="empty-sub">No se encontraron envíos para el departamento "<strong>@selectedDeptNombre</strong>"</p> </div> </div>
}

<!-- Tabla + Paginado server-side -->
@if (resultados != null && resultados.Any()) {
    <div class="results-section"> <div class="results-header"> <div class="results-count"> <span class="count-badge">@totalRegistros</span> <span class="count-text"> registro@(totalRegistros != 1 ? "s" : "") encontrado@(totalRegistros != 1 ? "s" : "") </span> </div> <div class="results-meta"> Departamento: <strong>@selectedDeptNombre</strong> &nbsp;
    · &nbsp;

    @fecInicio.ToString("dd/MM/yyyy") — @fecFin.ToString("dd/MM/yyyy") </div> </div> <div class="table-wrapper"> <table class="results-table"> <thead> <tr> <th>#</th> <th>N° de Cargo</th> <th>Destinatario</th> <th>Dirección</th> <th>Distrito</th> <th>Contenido</th> <th>Fec. Notificación</th> <th>Motivo</th> <th>Estado</th> <th>Acción</th> </tr> </thead> <tbody> @foreach (var (item, idx) in resultados.Select((x, i) => (x, i))) {
        int rowNum = (currentPage - 1) * pageSize + idx + 1;
        <tr class="table-row" style="animation-delay:@(idx * 0.04)s"> <td class="cell-num">@rowNum</td> <td><span class="cargo-badge">@item.Ccodbar</span></td> <td class="cell-dest">@item.Cnomdes</td> <td class="cell-dir">@item.Cdirecc</td> <td class="cell-dir">@item.Cnomdis</td> <td class="cell-cont">@item.Contenido</td> <td class="cell-fecha">@item.Cfecnot</td> <td class="cell-cont">@item.Cabrmot</td> <td> <span class="estado-pill @GetEstadoClass(item.CSituac)">@item.CSituac</span> </td> <td> <button class="btn-ver" @onclick="() => VerDetalle(item.Ccodbar)" title="Ver detalle"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" /> <circle cx="12" cy="12" r="3" /> </svg> Ver </button> </td> </tr>
    }

    </tbody> </table> </div>
    <!-- Paginado server-side -->
    @if (totalPages > 1)

{
    <div class="pagination-wrap"> <div class="page-info"> Página <strong>@currentPage</strong> de <strong>@totalPages</strong> &nbsp;
    · &nbsp;

    @((currentPage - 1) * pageSize + 1)–@(Math.Min(currentPage * pageSize, totalRegistros)) de @totalRegistros </div> <div class="page-btns"> <button class="page-btn" @onclick='() => GoToPage(1)' disabled="@(currentPage == 1 || isSearching)" title="Primera"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> <polyline points="11 17 6 12 11 7" /> <polyline points="18 17 13 12 18 7" /> </svg> </button> <button class="page-btn" @onclick='() => GoToPage(currentPage - 1)' disabled="@(currentPage == 1 || isSearching)" title="Anterior"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> <polyline points="15 18 9 12 15 6" /> </svg> </button> @foreach (var p in GetPageNumbers()) {
        @if (p == -1) {
            <span class="page-ellipsis">…</span>
        }

        else {
            <button class="page-btn @(p == currentPage ? "active" : "")" @onclick="() => GoToPage(p)" disabled="@isSearching"> @p </button>
        }
    }

    <button class="page-btn" @onclick='() => GoToPage(currentPage + 1)' disabled="@(currentPage == totalPages || isSearching)" title="Siguiente"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> <polyline points="9 18 15 12 9 6" /> </svg> </button> <button class="page-btn" @onclick='() => GoToPage(totalPages)' disabled="@(currentPage == totalPages || isSearching)" title="Última"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> <polyline points="13 17 18 12 13 7" /> <polyline points="6 17 11 12 6 7" /> </svg> </button> </div> </div>
}

</div >
}

<!-- Estado inicial -->
@if (!hasBuscado && !isSearching) {
    <div class="welcome-state"> <div class="welcome-icon"> <svg viewBox="0 0 80 80" fill="none"> <circle cx="40" cy="40" r="38" stroke="#e2e8f0" stroke-width="2" /> <path d="M40 20 C40 20 55 32 55 44 C55 52.8 48.3 58 40 58 C31.7 58 25 52.8 25 44 C25 32 40 20 40 20Z" stroke="#0A1B4B" stroke-width="2.5" fill="none" /> <circle cx="40" cy="44" r="5" stroke="#D40000" stroke-width="2.5" /> </svg> </div> <p class="welcome-title">Selecciona un departamento</p> <p class="welcome-sub">Elige el departamento y el rango de fechas para ver los envíos</p> </div>
}

</main >
</div >
<!-- ================================================================
MODAL DETALLE
================================================================ -->
@if (showModal) {
    <div class="modal-overlay" @onclick="CerrarModal"> <div class="modal-container" @onclick:stopPropagation="true"> <div class="modal-header"> <div class="modal-title-wrap"> <div class="modal-title-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /> <polyline points="14 2 14 8 20 8" /> <line x1="16" y1="13" x2="8" y2="13" /> <line x1="16" y1="17" x2="8" y2="17" /> </svg> </div> <div class="modal-title-text"> <h2>Detalle del Envío</h2> <p>@selectedCcodbar</p> </div> </div> <button class="modal-close" @onclick="CerrarModal" title="Cerrar"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> <line x1="18" y1="6" x2="6" y2="18" /> <line x1="6" y1="6" x2="18" y2="18" /> </svg> </button> </div> <div class="modal-body"> @if (isLoadingDetalle)

{
    <div class="modal-loading"> <div class="modal-spinner"></div> <p>Cargando detalle del envío...</p> </div>
}

else if (detalleActual != null) {
    <div class="modal-left"> <div class="detail-section"> <div class="detail-section-header"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <rect x="2" y="3" width="20" height="14" rx="2" ry="2" /> <line x1="8" y1="21" x2="16" y2="21" /> <line x1="12" y1="17" x2="12" y2="21" /> </svg> <span>Datos del Servicio</span> </div> <div class="detail-grid"> <div class="detail-field full-width"> <span class="field-label">N° de Cargo</span> <span class="field-value mono">@detalleActual.Ccodbar</span> </div> @if (!string.IsNullOrWhiteSpace(detalleActual.ClavE_CLIENTE))

{
    <div class="detail-field"> <span class="field-label">Clave Cliente</span> <span class="field-value">@detalleActual.ClavE_CLIENTE</span> </div>
}

<div class="detail-field" >
<span class="field-label" > Fecha Recepción</span >
<span class="field-value" > @detalleActual.Cfecrec</span >
</div >
<div class="detail-field" >
<span class="field-label" > Servicio</span >
<span class="field-value" > @detalleActual.Detalle_servicio</span >
</div >
<div class="detail-field" >
<span class="field-label" > Cliente</span >
<span class="field-value" > @detalleActual.Cliente.Trim()</span >
</div >
<div class="detail-field" >
<span class="field-label" > Oficina</span >
<span class="field-value" > @detalleActual.Oficina.Trim()</span >
</div >
@if (!string.IsNullOrWhiteSpace(detalleActual.Remitente)) {
    <div class="detail-field full-width"> <span class="field-label">Remitente</span> <span class="field-value">@detalleActual.Remitente</span> </div>
}

<div class="detail-field full-width" >
<span class="field-label" > Destinatario</span >
<span class="field-value" > @detalleActual.Cnomdes</span >
</div >
<div class="detail-field full-width" >
<span class="field-label" > Dirección</span >
<span class="field-value" > @detalleActual.Cdirecc</span >
</div >
<div class="detail-field" >
<span class="field-label" > Distrito</span >
<span class="field-value" > @detalleActual.Cnomdis</span >
</div >
<div class="detail-field" >
<span class="field-label" > Glosa / Correlativo</span >
<span class="field-value" > @(string.IsNullOrWhiteSpace(detalleActual.Glosa) ? "—" : detalleActual.Glosa)</span >
</div >
<div class="detail-field full-width" >
<span class="field-label" > Contenido</span >
<span class="field-value" > @detalleActual.Contenido</span >
</div >
</div >
</div >

<div class="detail-section" >
<div class="detail-section-header red" >
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" >
<path d="M5 12h14" / >
<path d="M12 5l7 7-7 7" / >
</svg >
<span > Datos de Entrega</span >
</div >
<div class="detail-grid" >
<div class="detail-field" >
<span class="field-label" > Situación</span >
<span class="situacion-badge @GetEstadoClass(detalleActual.Situacion)" >
@detalleActual.Situacion
</span >
</div >
<div class="detail-field" >
<span class="field-label" > Fecha Notificación</span >
<span class="field-value" > @(string.IsNullOrWhiteSpace(detalleActual.Notificado) ? "—" : detalleActual.Notificado)</span >
</div >
<div class="detail-field full-width" >
<span class="field-label" > Estado / Motivo</span >
<span class="field-value" > @(string.IsNullOrWhiteSpace(detalleActual.Estado_motivo) ? "—" : detalleActual.Estado_motivo)</span >
</div >
</div >
</div >
</div >

<div class="modal-right" >
<div class="image-panel" >
<div class="image-panel-header" >
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" >
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" / >
<circle cx="8.5" cy="8.5" r="1.5" / >
<polyline points="21 15 16 10 5 21" / >
</svg >
<span > Imagen del Documento</span >
</div >
<div class="image-body" >
@if (!string.IsNullOrWhiteSpace(detalleActual.ImagenBase64)) {
    <img class="doc-image" src="data:image/jpeg;base64,@detalleActual.ImagenBase64" alt="Imagen del envío" title="Click para ampliar" @onclick="() => AbrirLightbox(detalleActual.ImagenBase64, detalleActual.Ccodbar)" />
}

else {
    <div class="no-image-placeholder"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"> <rect x="3" y="3" width="18" height="18" rx="2" ry="2" /> <line x1="1" y1="1" x2="23" y2="23" /> </svg> <p>Imagen no disponible</p> <span>Este documento no tiene imagen registrada</span> </div>
}

</div >
@if (!string.IsNullOrWhiteSpace(detalleActual.ImagenBase64)) {
    <div class="image-hint"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="11" cy="11" r="8" /> <line x1="21" y1="21" x2="16.65" y2="16.65" /> <line x1="11" y1="8" x2="11" y2="14" /> <line x1="8" y1="11" x2="14" y2="11" /> </svg> Clic en la imagen para ampliar </div>
}

</div >
</div >
}

else if (!string.IsNullOrEmpty(errorDetalle)) {
    <div class="state-card error-card" style="grid-column:1/-1"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="10" /> <line x1="12" y1="8" x2="12" y2="12" /> <line x1="12" y1="16" x2="12.01" y2="16" /> </svg> <p>@errorDetalle</p> </div>
}

</div >
</div >
</div >
}

<!-- ================================================================
LIGHTBOX
================================================================ -->
@if (showLightbox) {
    <div class="lightbox-overlay" @onclick="CerrarLightbox"> <div class="lightbox-inner" @onclick:stopPropagation="true"> <button class="lightbox-close" @onclick="CerrarLightbox" title="Cerrar"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> <line x1="18" y1="6" x2="6" y2="18" /> <line x1="6" y1="6" x2="18" y2="18" /> </svg> </button> <img class="lightbox-img" src="data:image/jpeg;base64,@lightboxBase64" alt="Vista ampliada" /> <div class="lightbox-caption"> @lightboxCaption &nbsp;
    · &nbsp;
    Clic fuera para cerrar </div> </div> </div>
}

@code {
    // ── Modelos inline ───────────────────────────────────────────── private record DepartamentoItem(string Coddepartamento, string Departamento);

    private class DestinatarioPaginadoItem {
        public int Nro {
            get;
            set;
        }

        public string Ccodbar {
            get;
            set;
        }

        = "";

        public string Cfecrec {
            get;
            set;
        }

        = "";

        public string Nro_documento {
            get;
            set;
        }

        = "";

        public string Clave_cliente {
            get;
            set;
        }

        = "";

        public string Cnomdes {
            get;
            set;
        }

        = "";

        public string Cdirecc {
            get;
            set;
        }

        = "";

        public string Cnomdis {
            get;
            set;
        }

        = "";

        public string Contenido {
            get;
            set;
        }

        = "";

        public string Cfecnot {
            get;
            set;
        }

        = "";

        public string Cabrmot {
            get;
            set;
        }

        = "";

        public string CSituac {
            get;
            set;
        }

        = "";

        public string Cimagen {
            get;
            set;
        }

        = "";

        public string Nro_orden {
            get;
            set;
        }

        = "";

        public int Nro_registro {
            get;
            set;
        }
    }

    private class BuscarPaginadoResponse {
        public bool IsSuccess {
            get;
            set;
        }

        public BuscarPaginadoData? Data {
            get;
            set;
        }

        public string Message {
            get;
            set;
        }

        = "";
    }

    private class BuscarPaginadoData {
        public List<DestinatarioPaginadoItem > Data {
            get;
            set;
        }

        = new();

        public int TotalRegistros {
            get;
            set;
        }

        public int TotalPaginas {
            get;
            set;
        }

        public int PaginaActual {
            get;
            set;
        }

        public int RegistrosPorPagina {
            get;
            set;
        }
    }

    private class DepartamentosResponse {
        public bool IsSuccess {
            get;
            set;
        }

        public List<DepartamentoItem > Data {
            get;
            set;
        }

        = new();

        public string Message {
            get;
            set;
        }

        = "";
    }

    // ── Estado departamentos ─────────────────────────────────────── private List<DepartamentoItem> departamentos = new();
    private string deptInputText = string.Empty;
    private string selectedDeptCod = string.Empty;
    private string selectedDeptNombre = string.Empty;
    private bool showDeptDropdown = false;
    private int deptFocusedIndex = -1;
    private IEnumerable<DepartamentoItem> DeptFiltered => string.IsNullOrWhiteSpace(deptInputText) ? departamentos : departamentos.Where(d => d.Departamento.Trim().Contains(deptInputText.Trim(), StringComparison.OrdinalIgnoreCase) || d.Coddepartamento.Contains(deptInputText.Trim(), StringComparison.OrdinalIgnoreCase));
    // ── Filtros ──────────────────────────────────────────────────── private DateTime fecInicio = new DateTime(DateTime.Now.Year, 1, 1);
    private DateTime fecFin = DateTime.Now;
    // ── Resultados y paginado ────────────────────────────────────── private List<DestinatarioPaginadoItem>? resultados;
    private int totalRegistros = 0;
    private int totalPages = 0;
    private int currentPage = 1;
    private const int pageSize = 10;
    // ── Estado general ───────────────────────────────────────────── private bool isSearching = false;
    private bool hasBuscado = false;
    private string errorMessage = string.Empty;
    private UserData? userData;
    // ── Modal detalle ────────────────────────────────────────────── private bool showModal = false;
    private bool isLoadingDetalle = false;
    private string errorDetalle = string.Empty;
    private string selectedCcodbar = string.Empty;
    private DocDetalle? detalleActual = null;
    // ── Lightbox ─────────────────────────────────────────────────── private bool showLightbox = false;
    private string lightboxBase64 = string.Empty;
    private string lightboxCaption = string.Empty;

    // =========================================================
    // LIFECYCLE
    // =========================================================
    protected override async Task OnInitializedAsync() {
        var token = await AuthService.GetTokenAsync();

        if (string.IsNullOrEmpty(token)) {
            Navigation .NavigateTo("/login");
            return;
        }

        userData = await AuthService.GetUserDataAsync();
        await CargarDepartamentos();
    }

    private async Task CargarDepartamentos() {
        try {
            var token = await AuthService.GetTokenAsync();
            var http = HttpClientFactory.CreateClient("CaypeApi");
            http .DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            var resp = await http.GetFromJsonAsync<DepartamentosResponse > (
                "api/Destinatario/departamentos",
                new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true });

            if (resp?.IsSuccess == true)
            departamentos = resp.Data ?? new();
        }

        catch {
            errorMessage = "No se pudieron cargar los departamentos.";
        }
    }

    // =========================================================
    // DEPARTAMENTO – SEARCHABLE DROPDOWN
    // =========================================================
    private void OnDeptInput(ChangeEventArgs e) {
        deptInputText = e.Value?.ToString() ?? string.Empty;
        selectedDeptCod = string.Empty;
        selectedDeptNombre = string.Empty;
        showDeptDropdown = true;
        deptFocusedIndex = -1;
    }

    private void OnDeptFocus() {
        showDeptDropdown = true;
        deptFocusedIndex = -1;
    }

    private void OnDeptFocusOut(FocusEventArgs e) {
        // pequeño delay para que el click en opción se procese primero Task.Delay(150).ContinueWith(_ => {
            showDeptDropdown = false;
            // Si no hay selección válida, limpiar el texto
            if (string.IsNullOrEmpty(selectedDeptCod))
                deptInputText = string.Empty;
            InvokeAsync(StateHasChanged);
        });
    }

    private void OnDeptKeyDown(KeyboardEventArgs e) {
        var list = DeptFiltered.ToList();
        if (!list.Any()) return;

        switch (e.Key) {
            case "ArrowDown": showDeptDropdown = true;
            deptFocusedIndex = Math.Min(deptFocusedIndex + 1, list.Count - 1);
            break;
            case "ArrowUp": deptFocusedIndex = Math.Max(deptFocusedIndex - 1, 0);
            break;
            case "Enter": if (deptFocusedIndex >= 0 && deptFocusedIndex < list.Count) SelectDept(list[deptFocusedIndex]);
            break;
            case "Escape": showDeptDropdown = false;
            break;
        }
    }

    private void SelectDept(DepartamentoItem dep) {
        selectedDeptCod = dep.Coddepartamento;
        selectedDeptNombre = dep.Departamento.Trim();
        deptInputText = dep.Departamento.Trim();
        showDeptDropdown = false;
        deptFocusedIndex = -1;
    }

    private void ClearDept() {
        deptInputText = string.Empty;
        selectedDeptCod = string.Empty;
        selectedDeptNombre = string.Empty;
        resultados = null;
        hasBuscado = false;
        errorMessage = string.Empty;
        currentPage = 1;
    }

    // =========================================================
    // BÚSQUEDA / PAGINADO SERVER-SIDE
    // =========================================================
    private async Task Buscar() {
        if (string.IsNullOrWhiteSpace(selectedDeptCod)) return;
        currentPage = 1;
        await CargarPagina();
    }

    private async Task GoToPage(int page) {
        if (page < 1 || page > totalPages || isSearching) return;
        currentPage = page;
        await CargarPagina();
    }

    private async Task CargarPagina() {
        isSearching = true;
        hasBuscado = true;
        errorMessage = string.Empty;
        StateHasChanged();

        try {
            var token = await AuthService.GetTokenAsync();
            var http = HttpClientFactory.CreateClient("CaypeApi");
            http .DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            var body = new {
                pagina = currentPage, registrosPorPagina = pageSize, codigoCliente = userData?.CodCliente ?? "", codigoDepartamento = selectedDeptCod, fecInicio = fecInicio.ToString("yyyy-MM-dd"), fecFin = fecFin.ToString("yyyy-MM-dd")
            }

            ;
            var response = await http.PostAsJsonAsync("api/Destinatario/buscarPaginado", body);

            if (response.IsSuccessStatusCode) {
                var result = await response.Content.ReadFromJsonAsync<BuscarPaginadoResponse > (
                    new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true });

                if (result?.IsSuccess == true && result.Data != null) {
                    resultados = result.Data.Data;
                    totalRegistros = result.Data.TotalRegistros;
                    totalPages = result.Data.TotalPaginas;
                }

                else {
                    errorMessage = result?.Message ?? "No se encontraron resultados.";
                    resultados = null;
                }
            }

            else {
                errorMessage = $"Error del servidor ({(int)response.StatusCode}).";
                resultados = null;
            }
        }

        catch (Exception ex) {
            errorMessage = $"Error al consultar: {ex.Message}";
            resultados = null;
        }

        finally {
            isSearching = false;
        }
    }

    private List<int > GetPageNumbers() {
        var pages = new List<int>();

        if (totalPages <= 7) {
            for (int i = 1; i <= totalPages; i++) pages.Add(i);
            return pages;
        }

        pages .Add(1);
        if (currentPage > 3) pages.Add(-1);
        int from = Math.Max(2, currentPage - 1);
        int to = Math.Min(totalPages - 1, currentPage + 1);
        for (int i = from; i <= to; i++) pages.Add(i);
        if (currentPage < totalPages - 2) pages.Add(-1);
        pages .Add(totalPages);
        return pages;
    }

    // =========================================================
    // MODAL DETALLE + IMAGEN
    // =========================================================
    private async Task VerDetalle(string ccodbar) {
        showModal = true;
        isLoadingDetalle = true;
        errorDetalle = string.Empty;
        detalleActual = null;
        selectedCcodbar = ccodbar;
        StateHasChanged();

        try {
            var token = await AuthService.GetTokenAsync();
            var http = HttpClientFactory.CreateClient("CaypeApi");
            http .DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            var response = await http.PostAsJsonAsync( "api/Destinatario/buscarDocDetalle", new { busqueda = ccodbar.Trim() });

            if (response.IsSuccessStatusCode) {
                var result = await response.Content.ReadFromJsonAsync<DocDetalleResponse > (
                    new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true });

                if (result?.EsExitosa == true && result.Datos?.Any() == true)
                detalleActual = result.Datos.First();
                else
                errorDetalle = result?.Mensaje ?? "No se encontró información del documento.";
            }

            else {
                var body = await response.Content.ReadAsStringAsync();
                errorDetalle = $"Error del servidor ({(int)response.StatusCode}). {body}";
            }
        }

        catch (Exception ex) {
            errorDetalle = $"Error: {ex.Message}";
        }

        finally {
            isLoadingDetalle = false;
        }
    }

    private void CerrarModal() {
        showModal = false;
        detalleActual = null;
        errorDetalle = string.Empty;
        CerrarLightbox();
    }

    // =========================================================
    // LIGHTBOX
    // =========================================================
    private void AbrirLightbox(string base64, string caption) {
        lightboxBase64 = base64;
        lightboxCaption = caption;
        showLightbox = true;
    }

    private void CerrarLightbox() {
        showLightbox = false;
        lightboxBase64 = string.Empty;
        lightboxCaption = string.Empty;
    }

    // =========================================================
    // HELPERS
    // =========================================================
    private async Task Logout() {
        await AuthService.LogoutAsync();
        Navigation .NavigateTo("/login");
    }

    private string GetInitials(string name) {
        var parts = name.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        return parts.Length >= 2 ? $"{parts[0][0]}{parts[1][0]}".ToUpper() : name.Length > 0 ? name[0].ToString().ToUpper() : "?";
    }

    private string GetEstadoClass(string? estado) = > estado?.ToLower().Trim() switch {
        "entregado" => "estado-ok", "liquidado" => "estado-ok", "en tránsito" or "en transito" => "estado-transit", "pendiente" => "estado-pending", "devuelto" => "estado-returned", _ => "estado-default"
    }

    ;
}
