<!-- Vista: Alibaba Cloud OSS — Gestión de configuraciones + Explorador -->
<div x-data="ossApiApp()" x-init="init()" @keydown.escape.window="closeModal()" class="bg-white rounded-lg shadow">

    <!-- Overlay de carga -->
    <div x-show="loading || browserLoading" class="fixed inset-0 bg-gray-800 bg-opacity-75 flex justify-center items-center z-50">
        <img src="../img/loading.gif" alt="Cargando..." class="w-16 h-16" />
    </div>

    <div class="container mx-auto p-6 w-full">

        <!-- Header -->
        <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-4">
            <div>
                <h1 class="text-2xl font-bold">Alibaba Cloud OSS</h1>
                <p class="text-xs text-slate-500 mt-0.5">Gestiona configuraciones de acceso y explora archivos del bucket activo.</p>
            </div>
            <button x-show="activeTab === 'config'" @click="openAdd()"
                class="flex items-center gap-2 text-white text-sm font-medium px-4 py-2 rounded-lg"
                style="background-color:#8eb02f"
                onmouseover="this.style.backgroundColor='#6d8c24'"
                onmouseout="this.style.backgroundColor='#8eb02f'">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                    <path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/>
                </svg>
                Nueva configuración
            </button>
            <label x-show="activeTab === 'browser'" class="flex items-center gap-2 text-white text-sm font-medium px-4 py-2 rounded-lg cursor-pointer"
                style="background-color:#8eb02f"
                onmouseover="this.style.backgroundColor='#6d8c24'"
                onmouseout="this.style.backgroundColor='#8eb02f'">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                    <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"/>
                </svg>
                Subir archivo
                <input type="file" class="hidden" @change="uploadFile($event)" />
            </label>
        </div>

        <!-- Tabs -->
        <div class="border-b border-gray-200 mb-5">
            <nav class="-mb-px flex gap-5 text-sm">
                <button @click="activeTab = 'config'"
                    :class="activeTab === 'config' ? 'border-b-2 border-[#8eb02f] text-[#6d8c24] font-semibold' : 'text-gray-500 hover:text-gray-700'"
                    class="pb-2 transition-colors">Configuraciones</button>
                <button @click="switchToBrowser()"
                    :class="activeTab === 'browser' ? 'border-b-2 border-[#8eb02f] text-[#6d8c24] font-semibold' : 'text-gray-500 hover:text-gray-700'"
                    class="pb-2 transition-colors flex items-center gap-1.5">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z"/>
                    </svg>
                    Explorador de archivos
                </button>
            </nav>
        </div>

        <!-- Alerta de error -->
        <div x-show="errorMsg" class="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700" x-text="errorMsg"></div>

        <!-- Filtros (solo tab config) -->
        <div x-show="activeTab === 'config'" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-6 gap-3 mb-5">
            <input x-model="search" @keyup.enter="load()" type="text" placeholder="Buscar por nombre..."
                class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" />
            <select x-model="filtroProvider" @change="load()"
                class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]">
                <option value="">Todos los proveedores</option>
                <option value="s3">S3 / MinIO</option>
                <option value="alibaba">Alibaba Cloud</option>
            </select>
            <select x-model="filtroEstado" @change="load()"
                class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]">
                <option value="">Todos los estados</option>
                <option value="activo">Activo</option>
                <option value="inactivo">Inactivo</option>
            </select>
            <input x-model="dateFrom" @change="load()" type="date"
                class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" />
            <input x-model="dateTo" @change="load()" type="date"
                class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" />
            <button @click="load()" :disabled="loading"
                class="flex items-center justify-center gap-2 px-4 py-2 rounded-lg border border-gray-300 text-sm hover:bg-gray-50 transition disabled:opacity-40">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" :class="loading && 'animate-spin'" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                    <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"/>
                </svg>
                <span x-text="loading ? 'Cargando...' : 'Buscar'"></span>
            </button>
        </div>

        <!-- Tabla (solo tab config) -->
        <div x-show="activeTab === 'config'" class="overflow-x-auto">
            <table class="table-auto w-full text-sm">
                <thead class="border-b border-gray-200 text-left text-xs font-semibold text-gray-500 uppercase">
                    <tr>
                        <th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('name')">
                            <span class="inline-flex items-center gap-1">Nombre <template x-if="sortBy === 'name'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
                        </th>
                        <th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('provider')">
                            <span class="inline-flex items-center gap-1">Proveedor <template x-if="sortBy === 'provider'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
                        </th>
                        <th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('endpoint')">
                            <span class="inline-flex items-center gap-1">Endpoint <template x-if="sortBy === 'endpoint'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
                        </th>
                        <th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('bucket_name')">
                            <span class="inline-flex items-center gap-1">Bucket <template x-if="sortBy === 'bucket_name'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
                        </th>
                        <th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('region')">
                            <span class="inline-flex items-center gap-1">Región <template x-if="sortBy === 'region'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
                        </th>
                        <th class="py-2 px-3 cursor-pointer select-none hover:text-gray-700" @click="sort('is_active')">
                            <span class="inline-flex items-center gap-1">Estado <template x-if="sortBy === 'is_active'"><span x-text="sortDir === 'asc' ? '↑' : '↓'"></span></template></span>
                        </th>
                        <th class="py-2 px-3 text-right">Acciones</th>
                    </tr>
                </thead>
                <tbody>
                    <template x-if="items.length === 0 && !loading">
                        <tr>
                            <td colspan="7" class="py-8 text-center text-gray-400 text-sm">
                                Sin configuraciones. Haz clic en "Nueva configuración" para agregar una.
                            </td>
                        </tr>
                    </template>
                    <template x-for="item in items" :key="item.ID">
                        <tr class="border-b border-gray-100 hover:bg-gray-50 transition-colors">
                            <td class="py-2 px-3 font-medium text-gray-800" x-text="item.name"></td>
                            <td class="py-2 px-3">
                                <span x-text="item.provider === 's3' ? 'S3' : 'Alibaba'"
                                    :class="item.provider === 's3' ? 'bg-orange-100 text-orange-700' : 'bg-blue-100 text-blue-700'"
                                    class="text-xs px-2 py-0.5 rounded-full font-medium"></span>
                            </td>
                            <td class="py-2 px-3 font-mono text-xs text-blue-700 break-all" x-text="item.endpoint"></td>
                            <td class="py-2 px-3 text-xs text-gray-600" x-text="item.bucket_name"></td>
                            <td class="py-2 px-3 text-xs text-gray-500" x-text="item.region || '—'"></td>
                            <td class="py-2 px-3">
                                <span x-text="item.is_active ? 'Activo' : 'Inactivo'"
                                    :class="item.is_active ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-500'"
                                    class="text-xs px-2 py-0.5 rounded-full font-medium"></span>
                            </td>
                            <td class="py-2 px-3">
                                <div class="flex items-center gap-2 justify-end">
                                    <button @click="openEdit(item)" title="Editar"
                                        class="text-blue-500 hover:text-blue-700 transition-colors">
                                        <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                                            <path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
                                        </svg>
                                    </button>
                                    <button @click="confirmDelete(item.ID)" title="Eliminar"
                                        class="text-red-400 hover:text-red-600 transition-colors">
                                        <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                                            <path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
                                        </svg>
                                    </button>
                                </div>
                            </td>
                        </tr>
                    </template>
                </tbody>
            </table>
        </div>

        <!-- Paginación -->
        <div class="flex items-center justify-between mt-4 text-sm text-gray-500" x-show="activeTab === 'config' && totalPages > 1">
            <span x-text="`Página ${page} de ${totalPages} — ${total} registro(s)`"></span>
            <div class="flex gap-2">
                <button @click="prevPage()" :disabled="page <= 1"
                    class="px-3 py-1 border rounded text-xs disabled:opacity-40 hover:bg-gray-50">Anterior</button>
                <button @click="nextPage()" :disabled="page >= totalPages"
                    class="px-3 py-1 border rounded text-xs disabled:opacity-40 hover:bg-gray-50">Siguiente</button>
            </div>
        </div>

        <!-- ─── TAB: Explorador de archivos ──────────────────────────────────── -->
        <div x-show="activeTab === 'browser'">

            <!-- Selector de configuración OSS -->
            <div class="flex items-center gap-3 mb-4 p-3 bg-gray-50 rounded-lg border border-gray-200">
                <label class="text-xs font-medium text-gray-600">Configuración OSS:</label>
                <select x-model="browserConfigId" @change="switchConfig()"
                    class="flex-1 border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]">
                    <option value="">Seleccionar...</option>
                    <template x-for="cfg in activeConfigs" :key="cfg.ID">
                        <option :value="cfg.ID" x-text="`${cfg.name} (${cfg.provider === 's3' ? 'S3' : 'Alibaba'})`"></option>
                    </template>
                </select>
            </div>

            <!-- Breadcrumb / ruta actual -->
            <div class="flex items-center gap-1 text-sm mb-3 flex-wrap">
                <button @click="navigateTo('')" class="text-[#8eb02f] hover:underline font-medium">Raíz</button>
                <template x-for="(seg, i) in breadcrumbs()" :key="i">
                    <span class="flex items-center gap-1">
                        <span class="text-gray-400">/</span>
                        <button @click="navigateTo(seg.prefix)" class="text-[#8eb02f] hover:underline" x-text="seg.label"></button>
                    </span>
                </template>
            </div>

            <!-- Barra de herramientas explorador -->
            <div class="flex flex-wrap items-center gap-3 mb-4">
                <span class="text-xs text-gray-400" x-text="browserPrefix ? `Carpeta: ${browserPrefix}` : 'Bucket raíz'"></span>
                <button @click="browserLoad()" :disabled="browserLoading"
                    class="flex items-center gap-1.5 px-3 py-1.5 border border-gray-300 rounded-lg text-xs hover:bg-gray-50 transition disabled:opacity-40">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" :class="browserLoading && 'animate-spin'" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"/>
                    </svg>
                    Recargar
                </button>
            </div>

            <!-- Mensaje error explorador -->
            <div x-show="browserError" class="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700" x-text="browserError"></div>

            <!-- Lista de objetos -->
            <div class="border border-gray-200 rounded-lg overflow-hidden">
                <!-- Carpetas virtuales -->
                <template x-for="prefix in browserResult.prefixes || []" :key="prefix">
                    <div class="flex items-center gap-3 px-4 py-2.5 border-b border-gray-100 hover:bg-gray-50 cursor-pointer transition-colors"
                        @click="navigateTo(prefix)">
                        <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-yellow-400 flex-shrink-0" fill="currentColor" viewBox="0 0 24 24">
                            <path d="M19.5 21a3 3 0 003-3v-4.5a3 3 0 00-3-3h-15a3 3 0 00-3 3V18a3 3 0 003 3h15zM1.5 10.146V6a3 3 0 013-3h5.379a2.25 2.25 0 011.59.659l2.122 2.121c.14.141.331.22.53.22H19.5a3 3 0 013 3v1.146A4.483 4.483 0 0019.5 9h-15a4.483 4.483 0 00-3 1.146z"/>
                        </svg>
                        <span class="text-sm font-medium text-gray-700" x-text="folderName(prefix)"></span>
                        <span class="ml-auto text-xs text-gray-400">Carpeta →</span>
                    </div>
                </template>

                <!-- Archivos -->
                <template x-for="obj in browserResult.objects || []" :key="obj.key">
                    <div class="flex items-center gap-3 px-4 py-2.5 border-b border-gray-100 hover:bg-gray-50 transition-colors">
                        <span x-html="fileIcon(obj.key)" class="flex-shrink-0"></span>
                        <div class="flex-1 min-w-0">
                            <p class="text-sm text-gray-800 truncate" x-text="fileName(obj.key)"></p>
                            <p class="text-xs text-gray-400" x-text="obj.key"></p>
                        </div>
                        <span class="text-xs text-gray-500 flex-shrink-0" x-text="formatSize(obj.size)"></span>
                        <span class="text-xs text-gray-400 flex-shrink-0 hidden sm:block" x-text="formatDate(obj.last_modified)"></span>
                        <div class="flex items-center gap-2 flex-shrink-0">
                            <button @click="previewOrDownload(obj.key)" title="Ver / Descargar"
                                class="text-[#8eb02f] hover:text-[#6d8c24] transition-colors">
                                <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                                    <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3"/>
                                </svg>
                            </button>
                            <button @click="confirmBrowserDelete(obj.key)" title="Eliminar"
                                class="text-red-400 hover:text-red-600 transition-colors">
                                <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                                    <path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
                                </svg>
                            </button>
                        </div>
                    </div>
                </template>

                <!-- Vacío -->
                <div x-show="!browserLoading && (browserResult.objects || []).length === 0 && (browserResult.prefixes || []).length === 0"
                    class="py-10 text-center text-gray-400 text-sm">
                    Carpeta vacía
                </div>
            </div>

            <!-- Paginación explorador -->
            <div class="flex items-center justify-between mt-3 text-xs text-gray-500" x-show="browserResult.is_truncated || browserMarker">
                <span x-show="browserMarker" class="text-gray-400">Mostrando desde el marcador actual</span>
                <div class="flex gap-2 ml-auto">
                    <button @click="browserPrev()" :disabled="markerHistory.length === 0"
                        class="px-3 py-1 border rounded disabled:opacity-40 hover:bg-gray-50">← Anterior</button>
                    <button @click="browserNext()" :disabled="!browserResult.is_truncated"
                        class="px-3 py-1 border rounded disabled:opacity-40 hover:bg-gray-50">Siguiente →</button>
                </div>
            </div>
        </div>

    </div>

    <!-- ─── Modal crear / editar ─────────────────────────────────────────────── -->
    <div x-show="showModal" class="fixed inset-0 z-40 flex items-center justify-center bg-black bg-opacity-40 p-4">
        <div class="bg-white rounded-xl shadow-xl w-full max-w-lg" @click.stop>
            <div class="flex items-center justify-between px-6 py-4 border-b">
                <h2 class="text-lg font-semibold" x-text="editItem ? 'Editar configuración OSS' : 'Nueva configuración OSS'"></h2>
                <button @click="closeModal()" class="text-gray-400 hover:text-gray-600">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
                    </svg>
                </button>
            </div>
            <form @submit.prevent="save()" class="px-6 py-4 space-y-4">
                <!-- Nombre -->
                <div>
                    <label class="block text-xs font-medium text-gray-700 mb-1">Nombre / Alias <span class="text-red-500">*</span></label>
                    <input x-model="form.name" type="text" placeholder="ej. OSS Producción"
                        class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" required />
                </div>
                <!-- Provider -->
                <div>
                    <label class="block text-xs font-medium text-gray-700 mb-1">Proveedor <span class="text-red-500">*</span></label>
                    <select x-model="form.provider"
                        class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]">
                        <option value="alibaba">Alibaba Cloud OSS</option>
                        <option value="s3">S3-Compatible (MinIO, AWS S3, etc.)</option>
                    </select>
                </div>
                <!-- Endpoint -->
                <div>
                    <label class="block text-xs font-medium text-gray-700 mb-1">Endpoint <span class="text-red-500">*</span></label>
                    <input x-model="form.endpoint" type="text" x-bind:placeholder="form.provider === 'alibaba' ? 'ej. oss-cn-hangzhou.aliyuncs.com' : 'ej. https://minio.u-s.app'"
                        class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" required />
                </div>
                <!-- Access Key ID -->
                <div>
                    <label class="block text-xs font-medium text-gray-700 mb-1">
                        Access Key ID
                        <span x-show="!editItem" class="text-red-500">*</span>
                        <span x-show="editItem" class="text-gray-400 font-normal">(dejar vacío para mantener)</span>
                    </label>
                    <input x-model="form.access_key_id" type="text" placeholder="LTAI5t..."
                        class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm font-mono focus:outline-none focus:ring-1 focus:ring-[#8eb02f]"
                        :required="!editItem" />
                </div>
                <!-- Access Key Secret -->
                <div>
                    <label class="block text-xs font-medium text-gray-700 mb-1">
                        Access Key Secret
                        <span x-show="!editItem" class="text-red-500">*</span>
                        <span x-show="editItem" class="text-gray-400 font-normal">(dejar vacío para mantener)</span>
                    </label>
                    <input x-model="form.access_key_secret" type="password" placeholder="••••••••••••"
                        class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm font-mono focus:outline-none focus:ring-1 focus:ring-[#8eb02f]"
                        :required="!editItem" />
                </div>
                <!-- Bucket + Región -->
                <div class="grid grid-cols-2 gap-3">
                    <div>
                        <label class="block text-xs font-medium text-gray-700 mb-1">Bucket <span class="text-red-500">*</span></label>
                        <input x-model="form.bucket_name" type="text" placeholder="mi-bucket"
                            class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" required />
                    </div>
                    <div>
                        <label class="block text-xs font-medium text-gray-700 mb-1">Región</label>
                        <input x-model="form.region" type="text" placeholder="ej. cn-hangzhou"
                            class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f]" />
                    </div>
                </div>
                <!-- Notas -->
                <div>
                    <label class="block text-xs font-medium text-gray-700 mb-1">Notas</label>
                    <textarea x-model="form.notes" rows="2" placeholder="Observaciones opcionales..."
                        class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#8eb02f] resize-none"></textarea>
                </div>
                <!-- Activo -->
                <div class="flex items-center gap-3">
                    <label class="relative inline-flex items-center cursor-pointer">
                        <input type="checkbox" x-model="form.is_active" class="sr-only peer" />
                        <div class="w-10 h-5 bg-gray-200 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-5 peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-[#8eb02f]"></div>
                    </label>
                    <span class="text-sm text-gray-600">Configuración activa</span>
                </div>
                <!-- Error modal -->
                <p x-show="modalError" class="text-xs text-red-600" x-text="modalError"></p>
                <!-- Botones -->
                <div class="flex justify-end gap-3 pt-2">
                    <button type="button" @click="closeModal()"
                        class="px-4 py-2 text-sm border border-gray-300 rounded-lg hover:bg-gray-50 transition">Cancelar</button>
                    <button type="submit" :disabled="saving"
                        class="px-4 py-2 text-sm text-white rounded-lg transition disabled:opacity-50"
                        style="background-color:#8eb02f"
                        onmouseover="if(!this.disabled)this.style.backgroundColor='#6d8c24'"
                        onmouseout="this.style.backgroundColor='#8eb02f'">
                        <span x-text="saving ? 'Guardando...' : 'Guardar'"></span>
                    </button>
                </div>
            </form>
        </div>
    </div>

    <!-- ─── Modal confirmación eliminar ─────────────────────────────────────── -->
    <div x-show="deleteId !== null" class="fixed inset-0 z-40 flex items-center justify-center bg-black bg-opacity-40 p-4">
        <div class="bg-white rounded-xl shadow-xl w-full max-w-sm p-6 text-center">
            <svg xmlns="http://www.w3.org/2000/svg" class="w-12 h-12 mx-auto text-red-400 mb-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
                <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"/>
            </svg>
            <h3 class="text-base font-semibold text-gray-800 mb-1">¿Eliminar configuración?</h3>
            <p class="text-sm text-gray-500 mb-5">Esta acción no se puede deshacer.</p>
            <div class="flex gap-3 justify-center">
                <button @click="deleteId = null" class="px-4 py-2 text-sm border border-gray-300 rounded-lg hover:bg-gray-50">Cancelar</button>
                <button @click="doDelete()" :disabled="saving"
                    class="px-4 py-2 text-sm text-white bg-red-500 rounded-lg hover:bg-red-600 transition disabled:opacity-50">
                    <span x-text="saving ? 'Eliminando...' : 'Eliminar'"></span>
                </button>
            </div>
        </div>
    </div>
</div>

<script>
function ossApiApp() {
    return {
        // ── Tab activo ────────────────────────────────────────────
        activeTab: 'config',

        // ── CRUD Configuraciones ──────────────────────────────────
        items: [],
        total: 0,
        totalPages: 1,
        page: 1,
        search: '',
        filtroProvider: '',
        filtroEstado: '',
        dateFrom: '',
        dateTo: '',
        sortBy: 'id',
        sortDir: 'desc',
        loading: false,
        errorMsg: '',
        showModal: false,
        editItem: null,
        saving: false,
        modalError: '',
        deleteId: null,
        form: {
            name: '', endpoint: '', access_key_id: '', access_key_secret: '',
            bucket_name: '', region: '', is_active: true, notes: '',
        },

        // ── Explorador de archivos ────────────────────────────────
        browserLoading: false,
        browserError: '',
        browserPrefix: '',
        browserMarker: '',
        markerHistory: [],
        browserResult: { objects: [], prefixes: [], is_truncated: false, next_marker: '' },
        browserDeleteKey: null,
        browserConfigId: '',
        activeConfigs: [],

        async init() {
            await this.load();
        },

        // ─ Configs ─────────────────────────────────────────────────
        async load() {
            this.loading = true;
            this.errorMsg = '';
            try {
                const params = new URLSearchParams({
                    page: this.page,
                    search: this.search,
                    provider: this.filtroProvider,
                    estado: this.filtroEstado,
                    date_from: this.dateFrom,
                    date_to: this.dateTo,
                    sort_by: this.sortBy,
                    sort_dir: this.sortDir,
                });
                const res = await fetch(`/app/loadossapi?${params}`);
                const data = await res.json();
                if (!res.ok) throw new Error(data.error || 'Error al cargar');
                this.items = data.items || [];
                this.total = data.total || 0;
                this.totalPages = data.totalPages || 1;
            } catch (e) {
                this.errorMsg = e.message;
            } finally {
                this.loading = false;
            }
        },

        sort(col) {
            if (this.sortBy === col) {
                this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc';
            } else {
                this.sortBy = col;
                this.sortDir = 'asc';
            }
            this.page = 1;
            this.load();
        },

        openAdd() {
            this.editItem = null;
            this.form = { name: '', provider: 'alibaba', endpoint: '', access_key_id: '', access_key_secret: '', bucket_name: '', region: '', is_active: true, notes: '' };
            this.modalError = '';
            this.showModal = true;
        },

        openEdit(item) {
            this.editItem = item;
            this.form = { name: item.name || '', provider: item.provider || 'alibaba', endpoint: item.endpoint || '', access_key_id: '', access_key_secret: '', bucket_name: item.bucket_name || '', region: item.region || '', is_active: item.is_active, notes: item.notes || '' };
            this.modalError = '';
            this.showModal = true;
        },

        closeModal() {
            this.showModal = false;
            this.editItem = null;
            this.modalError = '';
        },

        async save() {
            this.saving = true;
            this.modalError = '';
            try {
                const url = this.editItem ? `/app/oss-api/${this.editItem.ID}` : '/app/oss-api';
                const method = this.editItem ? 'PUT' : 'POST';
                const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(this.form) });
                const data = await res.json();
                if (!res.ok) throw new Error(data.error || 'Error al guardar');
                this.closeModal();
                await this.load();
            } catch (e) {
                this.modalError = e.message;
            } finally {
                this.saving = false;
            }
        },

        confirmDelete(id) { this.deleteId = id; },

        async doDelete() {
            if (!this.deleteId) return;
            this.saving = true;
            try {
                const res = await fetch(`/app/oss-api/${this.deleteId}`, { method: 'DELETE' });
                const data = await res.json();
                if (!res.ok) throw new Error(data.error || 'Error al eliminar');
                this.deleteId = null;
                await this.load();
            } catch (e) {
                this.errorMsg = e.message;
                this.deleteId = null;
            } finally {
                this.saving = false;
            }
        },

        prevPage() { if (this.page > 1) { this.page--; this.load(); } },
        nextPage() { if (this.page < this.totalPages) { this.page++; this.load(); } },

        // ─ Explorador ──────────────────────────────────────────────
        async switchToBrowser() {
            this.activeTab = 'browser';
            try {
                const res = await fetch('/app/oss-api/active');
                const data = await res.json();
                this.activeConfigs = data.data || [];
                if (this.activeConfigs.length > 0 && !this.browserConfigId) {
                    this.browserConfigId = this.activeConfigs[0].ID;
                }
                if ((this.browserResult.objects || []).length === 0 && (this.browserResult.prefixes || []).length === 0) {
                    this.browserLoad();
                }
            } catch (e) {
                this.browserError = e.message;
            }
        },

        switchConfig() {
            this.browserPrefix = '';
            this.browserMarker = '';
            this.markerHistory = [];
            this.browserResult = { objects: [], prefixes: [], is_truncated: false, next_marker: '' };
            this.browserLoad();
        },

        ossApiParams(extra) {
            const p = new URLSearchParams(extra || {});
            if (this.browserConfigId) p.set('oss_api_id', this.browserConfigId);
            return p;
        },

        async browserLoad() {
            this.browserLoading = true;
            this.browserError = '';
            try {
                const params = this.ossApiParams({ prefix: this.browserPrefix, marker: this.browserMarker, max_keys: 100 });
                const res = await fetch(`/app/oss-api/browser?${params}`);
                const data = await res.json();
                if (!res.ok) throw new Error(data.error || 'Error al listar objetos');
                this.browserResult = { objects: data.objects || [], prefixes: data.prefixes || [], is_truncated: data.is_truncated || false, next_marker: data.next_marker || '' };
            } catch (e) {
                this.browserError = e.message;
            } finally {
                this.browserLoading = false;
            }
        },

        navigateTo(prefix) {
            this.markerHistory = [];
            this.browserMarker = '';
            this.browserPrefix = prefix;
            this.browserLoad();
        },

        browserNext() {
            if (!this.browserResult.is_truncated) return;
            this.markerHistory.push(this.browserMarker);
            this.browserMarker = this.browserResult.next_marker;
            this.browserLoad();
        },

        browserPrev() {
            if (this.markerHistory.length === 0) return;
            this.browserMarker = this.markerHistory.pop();
            this.browserLoad();
        },

        breadcrumbs() {
            if (!this.browserPrefix) return [];
            const parts = this.browserPrefix.replace(/\/$/, '').split('/').filter(Boolean);
            return parts.map((label, i) => ({ label, prefix: parts.slice(0, i + 1).join('/') + '/' }));
        },

        async previewOrDownload(key) {
            try {
                const params = this.ossApiParams({ key });
                const res = await fetch(`/app/oss-api/browser/url?${params}`);
                const data = await res.json();
                if (!res.ok) throw new Error(data.error || 'Error generando URL');
                window.open(data.url, '_blank');
            } catch (e) {
                this.browserError = e.message;
            }
        },

        confirmBrowserDelete(key) { this.browserDeleteKey = key; this.deleteId = 'browser:' + key; },

        async doDelete() {
            if (!this.deleteId) return;
            this.saving = true;
            try {
                if (String(this.deleteId).startsWith('browser:')) {
                    const key = this.browserDeleteKey;
                    const params = this.ossApiParams({});
                    const res = await fetch(`/app/oss-api/browser/object?${params}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key }) });
                    const data = await res.json();
                    if (!res.ok) throw new Error(data.error || 'Error al eliminar');
                    this.deleteId = null;
                    this.browserDeleteKey = null;
                    await this.browserLoad();
                } else {
                    const res = await fetch(`/app/oss-api/${this.deleteId}`, { method: 'DELETE' });
                    const data = await res.json();
                    if (!res.ok) throw new Error(data.error || 'Error al eliminar');
                    this.deleteId = null;
                    await this.load();
                }
            } catch (e) {
                this.errorMsg = e.message;
                this.deleteId = null;
            } finally {
                this.saving = false;
            }
        },

        async uploadFile(event) {
            const file = event.target.files[0];
            if (!file) return;
            this.browserLoading = true;
            this.browserError = '';
            try {
                const fd = new FormData();
                fd.append('file', file);
                fd.append('prefix', this.browserPrefix);
                if (this.browserConfigId) fd.append('oss_api_id', this.browserConfigId);
                const res = await fetch('/app/oss-api/browser/upload', { method: 'POST', body: fd });
                const data = await res.json();
                if (!res.ok) throw new Error(data.error || 'Error al subir archivo');
                await this.browserLoad();
            } catch (e) {
                this.browserError = e.message;
            } finally {
                this.browserLoading = false;
                event.target.value = '';
            }
        },

        // ─ Helpers de UI ───────────────────────────────────────────
        folderName(prefix) { return prefix.replace(/\/$/, '').split('/').pop() || prefix; },
        fileName(key) { return key.split('/').pop() || key; },

        formatSize(bytes) {
            if (bytes === 0) return '0 B';
            const k = 1024, sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return (bytes / Math.pow(k, i)).toFixed(i === 0 ? 0 : 1) + ' ' + sizes[i];
        },

        formatDate(iso) {
            if (!iso) return '—';
            return new Date(iso).toLocaleDateString('es', { year: 'numeric', month: 'short', day: 'numeric' });
        },

        fileIcon(key) {
            const ext = (key.split('.').pop() || '').toLowerCase();
            const img = ['jpg','jpeg','png','gif','webp','svg','bmp','ico'];
            const vid = ['mp4','avi','mov','mkv','webm'];
            const doc = ['pdf','doc','docx','xls','xlsx','ppt','pptx'];
            const arc = ['zip','tar','gz','rar','7z'];
            const cod = ['js','ts','go','py','json','yaml','yml','html','css','xml','sh'];
            if (img.includes(ext)) return `<svg class="w-5 h-5 text-pink-400" fill="currentColor" viewBox="0 0 24 24"><path d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M2.25 18.75V6.75A2.25 2.25 0 014.5 4.5h15A2.25 2.25 0 0121.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25H4.5A2.25 2.25 0 012.25 18.75zM16.5 8.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0z"/></svg>`;
            if (vid.includes(ext)) return `<svg class="w-5 h-5 text-purple-400" fill="currentColor" viewBox="0 0 24 24"><path d="M3 8.25V18a2.25 2.25 0 002.25 2.25h13.5A2.25 2.25 0 0021 18V8.25M3 8.25L12 3l9 5.25M3 8.25h18"/></svg>`;
            if (doc.includes(ext)) return `<svg class="w-5 h-5 text-blue-400" fill="currentColor" viewBox="0 0 24 24"><path d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"/></svg>`;
            if (arc.includes(ext)) return `<svg class="w-5 h-5 text-orange-400" fill="currentColor" viewBox="0 0 24 24"><path d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z"/></svg>`;
            if (cod.includes(ext)) return `<svg class="w-5 h-5 text-green-500" fill="currentColor" viewBox="0 0 24 24"><path d="M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z"/></svg>`;
            return `<svg class="w-5 h-5 text-gray-400" fill="currentColor" viewBox="0 0 24 24"><path d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"/></svg>`;
        },
    };
}
</script>
