<template>
  <div class="view-container">
    <div class="view-header">
      <h2>Administration</h2>
    </div>

    <!-- App Settings -->
    <div class="admin-section">
      <h3>Registrierung</h3>
      <div class="setting-row">
        <div class="setting-info">
          <strong>Oeffentliche Registrierung</strong>
          <p>Wenn aktiviert, koennen sich neue Benutzer selbst registrieren. Andernfalls kann nur ein Admin neue Benutzer anlegen oder Einladungslinks versenden.</p>
        </div>
        <InputSwitch v-model="publicRegistration" @change="saveSettings" />
      </div>

      <div class="invite-section">
        <h4>Einladungslink generieren</h4>
        <p class="hint">Einmal-Links funktionieren auch bei deaktivierter oeffentlicher Registrierung.</p>
        <div class="invite-row">
          <InputText v-model="inviteEmail" placeholder="E-Mail zum Versenden (optional)" fluid />
          <Button label="Link erstellen" icon="pi pi-link" size="small" @click="createInvite" :loading="inviteLoading" />
        </div>
        <div v-if="inviteResult" class="invite-result">
          <div class="invite-url">
            <code>{{ fullInviteUrl }}</code>
            <Button icon="pi pi-copy" text size="small" @click="copyInviteLink" />
          </div>
          <Message v-if="inviteResult.email_sent" severity="success" :closable="false">
            Einladung an {{ inviteEmail }} gesendet
          </Message>
          <Message v-if="inviteResult.email_sent === false" severity="warn" :closable="false">
            Link erstellt, aber E-Mail konnte nicht gesendet werden: {{ inviteResult.email_error }}
          </Message>
        </div>
      </div>
    </div>

    <!-- System Email -->
    <div class="admin-section">
      <h3>System-E-Mail (SMTP)</h3>
      <p class="hint">Wird fuer Einladungslinks und System-Benachrichtigungen verwendet.</p>
      <div class="smtp-form">
        <div class="field-row">
          <div class="field flex-grow">
            <label>SMTP-Server</label>
            <InputText v-model="smtpForm.system_smtp_host" placeholder="smtp.beispiel.de" fluid />
          </div>
          <div class="field" style="width: 100px">
            <label>Port</label>
            <InputText v-model.number="smtpForm.system_smtp_port" type="number" fluid />
          </div>
          <div class="field" style="width: 80px">
            <label>SSL</label>
            <InputSwitch v-model="smtpForm.system_smtp_ssl" />
          </div>
        </div>
        <div class="field">
          <label>Benutzername</label>
          <InputText v-model="smtpForm.system_smtp_username" fluid />
        </div>
        <div class="field">
          <label>Passwort {{ smtpPasswordSet ? '(gesetzt - leer lassen = nicht aendern)' : '' }}</label>
          <Password v-model="smtpForm.system_smtp_password" :feedback="false" toggle-mask fluid />
        </div>
        <div class="field">
          <label>Absender-Adresse</label>
          <InputText v-model="smtpForm.system_email_from" placeholder="noreply@beispiel.de" fluid />
        </div>
        <div class="smtp-actions">
          <Button label="Speichern" icon="pi pi-save" size="small" @click="saveSmtp" />
          <Button label="Verbindung testen" icon="pi pi-check-circle" size="small" outlined @click="testSmtp" :loading="smtpTesting" />
        </div>
      </div>
    </div>

    <!-- OnlyOffice -->
    <div class="admin-section">
      <h3>OnlyOffice Document Server</h3>
      <p class="hint">Fuer die Bearbeitung von Word, Excel und PowerPoint Dateien direkt im Browser.
        Ohne OnlyOffice werden Dateien in einer einfachen Vorschau angezeigt.</p>

      <div class="setting-row">
        <div class="setting-info">
          <strong>Status</strong>
        </div>
        <Tag v-if="onlyofficeConfigured" value="Konfiguriert" severity="success" />
        <Tag v-else value="Nicht konfiguriert" severity="warn" />
      </div>

      <div v-if="onlyofficeConfigured" class="settings-info" style="margin: 0.75rem 0">
        <div class="info-row">
          <span class="label">URL:</span>
          <code>{{ onlyofficeUrl }}</code>
        </div>
        <div class="info-row">
          <span class="label">JWT:</span>
          <span>Nutzt JWT_SECRET_KEY aus .env</span>
        </div>
      </div>

      <div class="restore-instructions" style="margin-top: 1rem">
        <strong>Konfiguration ueber <code>.env</code>:</strong>
        <pre style="background: var(--p-surface-100); padding: 0.75rem; border-radius: 4px; font-size: 0.85rem; margin: 0.5rem 0">ONLYOFFICE_URL=https://office.deine-domain.de</pre>
        <p class="hint">JWT wird automatisch vom <code>JWT_SECRET_KEY</code> verwendet - kein extra Secret noetig.</p>
        <strong>Setup-Schritte:</strong>
        <ol>
          <li>In <code>docker-compose.yml</code> den <code>onlyoffice</code>-Service aktivieren</li>
          <li><code>ONLYOFFICE_URL</code> und <code>ONLYOFFICE_JWT_SECRET</code> in <code>.env</code> setzen</li>
          <li>Nginx-Eintrag fuer die OnlyOffice-Domain anlegen (siehe <code>nginx.example.conf</code>)</li>
          <li>Let's Encrypt: <code>certbot --nginx -d office.deine-domain.de</code></li>
          <li><code>docker-compose up --build -d</code></li>
        </ol>
      </div>
    </div>

    <!-- Backup & Restore -->
    <div class="admin-section">
      <h3>Backup & Restore</h3>

      <div class="backup-grid">
        <!-- Backup -->
        <div class="backup-card">
          <h4><i class="pi pi-download"></i> Backup erstellen</h4>
          <p>Erstellt eine ZIP-Datei mit der kompletten Datenbank und allen hochgeladenen Dateien.</p>
          <Button label="Backup herunterladen" icon="pi pi-download" @click="createBackup" :loading="backupLoading" />
        </div>

        <!-- Restore -->
        <div class="backup-card">
          <h4><i class="pi pi-upload"></i> Restore</h4>

          <div class="restore-instructions">
            <strong>Anleitung:</strong>
            <ol>
              <li>Neue Mini-Cloud-Instanz aufsetzen (Docker oder manuell)</li>
              <li>Admin-Benutzer registrieren</li>
              <li><strong>Wichtig:</strong> In der <code>.env</code> muessen <code>SECRET_KEY</code> und <code>JWT_SECRET_KEY</code> identisch zur alten Instanz sein, sonst koennen verschluesselte Daten (E-Mail-Passwoerter, Passwort-Manager) nicht entschluesselt werden!</li>
              <li>Backup-ZIP hier hochladen</li>
              <li>Alle Benutzer, Dateien, Kalender, Kontakte und Einstellungen werden wiederhergestellt</li>
            </ol>
          </div>

          <Message v-if="!restoreInProgress" severity="warn" :closable="false">
            Die SECRET_KEY und JWT_SECRET_KEY in der .env muessen mit dem Backup uebereinstimmen!
          </Message>

          <div v-if="!restoreInProgress" class="field">
            <label>Backup-ZIP auswaehlen</label>
            <input ref="restoreFileInput" type="file" accept=".zip" @change="onRestoreFileSelected" />
          </div>

          <div v-if="restoreFile && !restoreInProgress" class="restore-buttons">
            <Button label="Einzelne Dateien durchsuchen" icon="pi pi-search" size="small" outlined @click="browseLocalBackup" :loading="browseLoading" />
          </div>

          <div v-if="restoreFile && !restoreInProgress" class="restore-info">
            <p>Datei: <strong>{{ restoreFile.name }}</strong> ({{ formatSize(restoreFile.size) }})</p>
            <Button label="Restore starten" icon="pi pi-upload" severity="warn" @click="startRestore" />
          </div>

          <div v-if="restoreInProgress" class="restore-progress">
            <p><i class="pi pi-spin pi-spinner"></i> Restore laeuft...</p>
            <ProgressBar :value="restoreProgress" />
            <p class="progress-text">{{ restoreStatus }}</p>
          </div>

          <div v-if="restoreResult" class="restore-result">
            <Message :severity="restoreResult.success ? 'success' : 'error'" :closable="false">
              {{ restoreResult.message }}
            </Message>
            <div v-if="restoreResult.tables?.length" class="result-details">
              <strong>Wiederhergestellte Tabellen:</strong>
              <ul>
                <li v-for="t in restoreResult.tables" :key="t.name">{{ t.name }}: {{ t.rows }} Eintraege</li>
              </ul>
              <p v-if="restoreResult.files_disk">Dateien auf Festplatte: {{ restoreResult.files_disk }}</p>
              <p>Backup vom: {{ restoreResult.backup_date }}</p>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- SFTP Backup Targets -->
    <div class="admin-section">
      <div class="section-header">
        <h3>SFTP-Backup-Ziele</h3>
        <Button icon="pi pi-plus" label="Ziel hinzufuegen" size="small" @click="openNewTarget" />
      </div>
      <p class="hint">Automatische Backups werden im eingestellten Intervall auf SFTP-Server hochgeladen. Mehrere Ziele moeglich.</p>

      <div v-if="!sftpTargets.length" class="empty-hint-small">
        Keine SFTP-Backup-Ziele konfiguriert.
      </div>

      <div v-for="tgt in sftpTargets" :key="tgt.id" class="sftp-target-card">
        <div class="target-header">
          <div class="target-info">
            <strong>{{ tgt.name }}</strong>
            <span class="target-detail">{{ tgt.username }}@{{ tgt.host }}:{{ tgt.port }}{{ tgt.remote_path }}</span>
          </div>
          <div class="target-status">
            <Tag v-if="tgt.is_active" value="Aktiv" severity="success" />
            <Tag v-else value="Inaktiv" severity="warn" />
            <Tag :value="intervalLabel(tgt.backup_interval_minutes)" severity="info" />
          </div>
        </div>

        <div v-if="tgt.last_backup_at" class="target-last-backup">
          <i :class="tgt.last_backup_status === 'success' ? 'pi pi-check-circle' : 'pi pi-times-circle'"
             :style="{ color: tgt.last_backup_status === 'success' ? 'var(--p-green-500)' : 'var(--p-red-500)' }"></i>
          Letztes Backup: {{ formatDateTime(tgt.last_backup_at) }}
          <span v-if="tgt.last_backup_message" class="backup-msg">- {{ tgt.last_backup_message }}</span>
        </div>

        <div class="target-actions">
          <Button icon="pi pi-play" label="Jetzt sichern" size="small" outlined @click="runBackupNow(tgt)" :loading="tgt._running" />
          <Button icon="pi pi-list" label="Versionen" size="small" outlined @click="openVersions(tgt)" />
          <Button icon="pi pi-check-circle" label="Testen" size="small" text @click="testTarget(tgt)" />
          <Button icon="pi pi-pencil" text size="small" @click="openEditTarget(tgt)" />
          <Button icon="pi pi-trash" text size="small" severity="danger" @click="confirmDeleteSftp(tgt)" />
        </div>
      </div>
    </div>

    <!-- SFTP Target Dialog -->
    <Dialog v-model:visible="showTargetDialog" :header="editingTarget ? 'Backup-Ziel bearbeiten' : 'Neues Backup-Ziel'" modal :style="{ width: '550px' }">
      <div class="field">
        <label>Name</label>
        <InputText v-model="targetForm.name" placeholder="z.B. Hetzner Storage Box" fluid autofocus />
      </div>
      <div class="field-row">
        <div class="field flex-grow">
          <label>SFTP-Host</label>
          <InputText v-model="targetForm.host" placeholder="backup.example.com" fluid />
        </div>
        <div class="field" style="width: 100px">
          <label>Port</label>
          <InputText v-model.number="targetForm.port" type="number" fluid />
        </div>
      </div>
      <div class="field">
        <label>Benutzername</label>
        <InputText v-model="targetForm.username" fluid />
      </div>
      <div class="field">
        <label>{{ editingTarget?.has_password ? 'Passwort (leer = nicht aendern)' : 'Passwort' }}</label>
        <Password v-model="targetForm.password" :feedback="false" toggle-mask fluid />
      </div>
      <div class="field">
        <label>Remote-Pfad</label>
        <InputText v-model="targetForm.remote_path" placeholder="/backups/minicloud" fluid />
      </div>
      <div class="field-row">
        <div class="field flex-grow">
          <label>Backup-Intervall</label>
          <Select v-model="targetForm.backup_interval_minutes" :options="intervalOptions" optionLabel="label" optionValue="value" fluid />
        </div>
        <div class="field" style="width: 140px">
          <label>Max. Versionen</label>
          <InputText v-model.number="targetForm.max_versions" type="number" fluid />
        </div>
      </div>
      <div class="field">
        <label>Aktiv</label>
        <InputSwitch v-model="targetForm.is_active" />
      </div>
      <Message v-if="targetError" severity="error" :closable="false">{{ targetError }}</Message>
      <template #footer>
        <Button label="Abbrechen" text @click="showTargetDialog = false" />
        <Button :label="editingTarget ? 'Speichern' : 'Hinzufuegen'" @click="saveTarget" :loading="targetSaving" />
      </template>
    </Dialog>

    <!-- Versions Dialog -->
    <Dialog v-model:visible="showVersionsDialog" header="Backup-Versionen" modal :style="{ width: '600px' }">
      <div v-if="versionsLoading" class="loading-center">
        <i class="pi pi-spin pi-spinner"></i> Lade Versionen...
      </div>
      <div v-else-if="!versions.length" class="empty-hint-small">
        Keine Backups auf diesem Server gefunden.
      </div>
      <div v-else>
        <p class="hint">Ziel: <strong>{{ versionsTarget?.name }}</strong></p>
        <div v-for="ver in versions" :key="ver.name" class="version-item">
          <div class="version-info">
            <strong>{{ ver.name }}</strong>
            <span>{{ formatSize(ver.size) }} | {{ formatDateTime(ver.modified) }}</span>
          </div>
          <div class="version-actions">
            <Button label="Durchsuchen" icon="pi pi-search" size="small" outlined
                    @click="browseSftpVersion(ver)" :loading="ver._browsing" />
            <Button label="Komplett-Restore" icon="pi pi-download" size="small" severity="warn"
                    @click="restoreFromVersion(ver)" :loading="ver._restoring" />
          </div>
        </div>
      </div>
    </Dialog>

    <!-- Backup File Browser Dialog -->
    <Dialog v-model:visible="showFileBrowser" header="Backup-Inhalt durchsuchen" modal :style="{ width: '750px' }" @hide="closeBrowseSession">
      <div v-if="browseLoading" class="loading-center">
        <i class="pi pi-spin pi-spinner"></i> Lade Backup-Inhalt...
      </div>
      <div v-else>
        <div class="browse-toolbar">
          <InputText v-model="browseFilter" placeholder="Dateien filtern..." fluid />
          <span class="browse-count">{{ filteredBrowseFiles.length }} / {{ browseFiles.length }} Dateien</span>
        </div>

        <div class="browse-file-list">
          <div v-for="f in filteredBrowseFiles" :key="f.path" class="browse-file-item">
            <div class="browse-file-info">
              <i :class="browseFileIcon(f.path)"></i>
              <div class="browse-file-detail">
                <span class="browse-file-path">{{ f.path }}</span>
                <span class="browse-file-meta">{{ formatSize(f.size) }}</span>
              </div>
            </div>
            <div class="browse-file-actions">
              <Button icon="pi pi-download" label="Download" text size="small" @click="downloadFromBrowse(f)" />
              <Button v-if="f.path.startsWith('files/')" icon="pi pi-replay" label="Restore" text size="small" severity="warn" @click="restoreSingleFile(f)" />
            </div>
          </div>
          <div v-if="!filteredBrowseFiles.length" class="empty-hint-small">Keine Dateien gefunden.</div>
        </div>
      </div>
    </Dialog>

    <!-- User Management -->
    <div class="admin-section">
      <div class="section-header">
        <h3>Benutzerverwaltung</h3>
        <div class="header-actions">
          <InputText v-model="userSearch" placeholder="Benutzer suchen..." class="search-input" />
          <Button icon="pi pi-user-plus" label="Benutzer hinzufuegen" size="small" @click="openNewUser" />
        </div>
      </div>

      <DataTable :value="filteredUsers" :loading="loading" striped-rows>
        <Column field="id" header="ID" style="width: 60px" />
        <Column field="username" header="Benutzername" />
        <Column field="email" header="E-Mail">
          <template #body="{ data }">{{ data.email || '-' }}</template>
        </Column>
        <Column field="role" header="Rolle">
          <template #body="{ data }">
            <Tag :value="data.role" :severity="data.role === 'admin' ? 'danger' : 'info'" />
          </template>
        </Column>
        <Column field="is_active" header="Aktiv">
          <template #body="{ data }">
            <Tag :value="data.is_active ? 'Ja' : 'Nein'" :severity="data.is_active ? 'success' : 'warn'" />
          </template>
        </Column>
        <Column field="storage_quota_mb" header="Quota (MB)" />
        <Column header="Aktionen" style="width: 120px">
          <template #body="{ data }">
            <Button icon="pi pi-pencil" text rounded size="small" @click.stop="openEditUser(data)" />
            <Button icon="pi pi-trash" text rounded size="small" severity="danger" @click.stop="confirmDeleteUser(data)" />
          </template>
        </Column>
      </DataTable>
    </div>

    <!-- New/Edit User Dialog -->
    <Dialog v-model:visible="showUserDialog" :header="editingUser ? 'Benutzer bearbeiten' : 'Neuer Benutzer'" modal :style="{ width: '600px' }">
      <TabView>
        <TabPanel header="Allgemein">
          <div class="field">
            <label>Benutzername</label>
            <InputText v-model="userForm.username" fluid :disabled="!!editingUser" />
          </div>
          <div class="field">
            <label>E-Mail (optional)</label>
            <InputText v-model="userForm.email" type="email" fluid />
          </div>
          <div class="field">
            <label>{{ editingUser ? 'Neues Passwort (leer lassen = nicht aendern)' : 'Passwort' }}</label>
            <Password v-model="userForm.password" :feedback="false" toggle-mask fluid />
          </div>
          <div class="field">
            <label>Rolle</label>
            <Select v-model="userForm.role" :options="roleOptions" optionLabel="label" optionValue="value" fluid />
          </div>
          <div class="field">
            <label>Speicher-Quota (MB)</label>
            <InputText v-model.number="userForm.storage_quota_mb" type="number" fluid />
          </div>
          <div v-if="editingUser" class="field">
            <label>Aktiv</label>
            <InputSwitch v-model="userForm.is_active" />
          </div>
          <Message v-if="userError" severity="error" :closable="false">{{ userError }}</Message>
        </TabPanel>

        <TabPanel v-if="editingUser" header="E-Mail-Konten">
          <div class="email-tab">
            <div class="section-header">
              <span>Konten fuer {{ editingUser.username }}</span>
              <Button icon="pi pi-plus" label="Konto hinzufuegen" size="small" @click="openNewEmailAccount" />
            </div>

            <div v-if="!userEmailAccounts.length" class="empty-hint-small">
              Keine E-Mail-Konten konfiguriert.
            </div>

            <div v-for="acc in userEmailAccounts" :key="acc.id" class="email-account-row">
              <div class="acc-info">
                <strong>{{ acc.display_name }}</strong>
                <span>{{ acc.email_address }}</span>
              </div>
              <div class="acc-actions">
                <Button icon="pi pi-pencil" text size="small" @click="openEditEmailAccount(acc)" />
                <Button icon="pi pi-trash" text size="small" severity="danger" @click="confirmDeleteEmail(acc)" />
              </div>
            </div>
          </div>
        </TabPanel>
      </TabView>

      <template #footer>
        <Button label="Abbrechen" text @click="showUserDialog = false" />
        <Button :label="editingUser ? 'Speichern' : 'Erstellen'" @click="saveUser" :loading="userSaving" />
      </template>
    </Dialog>

    <!-- Email Account Sub-Dialog -->
    <Dialog v-model:visible="showEmailDialog" :header="editingEmailAcc ? 'E-Mail-Konto bearbeiten' : 'E-Mail-Konto hinzufuegen'" modal :style="{ width: '550px' }">
      <div class="field">
        <label>Anzeigename</label>
        <InputText v-model="emailForm.display_name" placeholder="z.B. Arbeit, Privat" fluid autofocus />
      </div>
      <div class="field">
        <label>E-Mail-Adresse</label>
        <InputText v-model="emailForm.email_address" type="email" fluid />
      </div>
      <h4 class="section-title">Posteingang (IMAP)</h4>
      <div class="field-row">
        <div class="field flex-grow">
          <label>Server</label>
          <InputText v-model="emailForm.imap_host" fluid />
        </div>
        <div class="field" style="width: 90px">
          <label>Port</label>
          <InputText v-model.number="emailForm.imap_port" type="number" fluid />
        </div>
        <div class="field" style="width: 70px">
          <label>SSL</label>
          <InputSwitch v-model="emailForm.imap_ssl" />
        </div>
      </div>
      <h4 class="section-title">Postausgang (SMTP)</h4>
      <div class="field-row">
        <div class="field flex-grow">
          <label>Server</label>
          <InputText v-model="emailForm.smtp_host" fluid />
        </div>
        <div class="field" style="width: 90px">
          <label>Port</label>
          <InputText v-model.number="emailForm.smtp_port" type="number" fluid />
        </div>
        <div class="field" style="width: 70px">
          <label>SSL</label>
          <InputSwitch v-model="emailForm.smtp_ssl" />
        </div>
      </div>
      <h4 class="section-title">Anmeldedaten</h4>
      <div class="field">
        <label>Benutzername</label>
        <InputText v-model="emailForm.username" fluid />
      </div>
      <div class="field">
        <label>{{ editingEmailAcc ? 'Passwort (leer = nicht aendern)' : 'Passwort' }}</label>
        <Password v-model="emailForm.password" :feedback="false" toggle-mask fluid />
      </div>
      <Message v-if="emailError" severity="error" :closable="false">{{ emailError }}</Message>
      <template #footer>
        <Button label="Abbrechen" text @click="showEmailDialog = false" />
        <Button :label="editingEmailAcc ? 'Speichern' : 'Hinzufuegen'" @click="saveEmailAccount" :loading="emailSaving" />
      </template>
    </Dialog>

    <!-- Delete Confirm -->
    <Dialog v-model:visible="showDeleteConfirm" header="Benutzer loeschen" modal :style="{ width: '400px' }">
      <p>Moechtest du den Benutzer <strong>{{ deleteTarget?.username }}</strong> wirklich loeschen?</p>
      <template #footer>
        <Button label="Abbrechen" text @click="showDeleteConfirm = false" />
        <Button label="Loeschen" severity="danger" @click="doDeleteUser" />
      </template>
    </Dialog>

    <!-- Confirm delete SFTP target -->
    <Dialog v-model:visible="showDeleteSftpConfirm" header="Backup-Ziel loeschen" modal :style="{ width: '400px' }">
      <p>Moechtest du das Backup-Ziel <strong>{{ deleteSftpTarget_ref?.name }}</strong> wirklich loeschen?</p>
      <template #footer>
        <Button label="Abbrechen" text @click="showDeleteSftpConfirm = false" />
        <Button label="Loeschen" severity="danger" @click="doDeleteSftpTarget" />
      </template>
    </Dialog>

    <!-- Confirm delete email account -->
    <Dialog v-model:visible="showDeleteEmailConfirm" header="E-Mail-Konto loeschen" modal :style="{ width: '400px' }">
      <p>Moechtest du das E-Mail-Konto <strong>{{ deleteEmailTarget?.display_name }}</strong> wirklich loeschen?</p>
      <template #footer>
        <Button label="Abbrechen" text @click="showDeleteEmailConfirm = false" />
        <Button label="Loeschen" severity="danger" @click="doDeleteEmailTarget" />
      </template>
    </Dialog>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue'
import { useToast } from 'primevue/usetoast'
import apiClient from '../api/client'
import { useAuthStore } from '../stores/auth'
import DataTable from 'primevue/datatable'
import Column from 'primevue/column'
import Tag from 'primevue/tag'
import Button from 'primevue/button'
import Dialog from 'primevue/dialog'
import InputText from 'primevue/inputtext'
import Password from 'primevue/password'
import Select from 'primevue/select'
import InputSwitch from 'primevue/inputswitch'
import Message from 'primevue/message'
import TabView from 'primevue/tabview'
import TabPanel from 'primevue/tabpanel'
import ProgressBar from 'primevue/progressbar'

const toast = useToast()
const auth = useAuthStore()
const users = ref([])
const loading = ref(false)
const publicRegistration = ref(true)

// Invite links
const inviteEmail = ref('')
const inviteLoading = ref(false)
const inviteResult = ref(null)
const fullInviteUrl = computed(() => inviteResult.value ? `${window.location.origin}${inviteResult.value.url}` : '')

// System SMTP
const smtpForm = ref({
  system_smtp_host: '', system_smtp_port: 587, system_smtp_ssl: true,
  system_smtp_username: '', system_smtp_password: '', system_email_from: '',
})
const smtpPasswordSet = ref(false)
const onlyofficeConfigured = ref(false)
const onlyofficeUrl = ref('')
const smtpTesting = ref(false)

// Backup & Restore
const backupLoading = ref(false)
const restoreFileInput = ref(null)
const restoreFile = ref(null)
const restoreInProgress = ref(false)
const restoreProgress = ref(0)
const restoreStatus = ref('')
const restoreResult = ref(null)
const CHUNK_SIZE = 10 * 1024 * 1024 // 10 MB

// SFTP Backup Targets
const sftpTargets = ref([])
const showTargetDialog = ref(false)
const editingTarget = ref(null)
const targetForm = ref({
  name: '', host: '', port: 22, username: '', password: '',
  remote_path: '/backups/minicloud', is_active: true,
  backup_interval_minutes: 1440, max_versions: 10,
})
const targetError = ref('')
const targetSaving = ref(false)
const intervalOptions = [
  { label: 'Alle 15 Minuten', value: 15 },
  { label: 'Stuendlich', value: 60 },
  { label: 'Alle 6 Stunden', value: 360 },
  { label: 'Alle 12 Stunden', value: 720 },
  { label: 'Taeglich', value: 1440 },
  { label: 'Woechentlich', value: 10080 },
  { label: 'Deaktiviert', value: 0 },
]

const showVersionsDialog = ref(false)
const versionsTarget = ref(null)
const versions = ref([])
const versionsLoading = ref(false)

// File browser
const showFileBrowser = ref(false)
const browseLoading = ref(false)
const browseId = ref('')
const browseFiles = ref([])
const browseFilter = ref('')
const filteredBrowseFiles = computed(() => {
  if (!browseFilter.value) return browseFiles.value
  const q = browseFilter.value.toLowerCase()
  return browseFiles.value.filter(f => f.path.toLowerCase().includes(q))
})

const showUserDialog = ref(false)
const editingUser = ref(null)
const userForm = ref({ username: '', email: '', password: '', role: 'user', storage_quota_mb: 5120, is_active: true })
const userError = ref('')
const userSaving = ref(false)
const roleOptions = [{ label: 'Benutzer', value: 'user' }, { label: 'Admin', value: 'admin' }]

const userSearch = ref('')
const filteredUsers = computed(() => {
  if (!userSearch.value) return users.value
  const q = userSearch.value.toLowerCase()
  return users.value.filter(u =>
    u.username.toLowerCase().includes(q) ||
    (u.email || '').toLowerCase().includes(q)
  )
})

const showDeleteConfirm = ref(false)
const deleteTarget = ref(null)
const showDeleteSftpConfirm = ref(false)
const deleteSftpTarget_ref = ref(null)
const showDeleteEmailConfirm = ref(false)
const deleteEmailTarget = ref(null)

// Email accounts for edited user
const userEmailAccounts = ref([])
const showEmailDialog = ref(false)
const editingEmailAcc = ref(null)
const emailForm = ref({
  display_name: '', email_address: '', imap_host: '', imap_port: 993, imap_ssl: true,
  smtp_host: '', smtp_port: 587, smtp_ssl: true, username: '', password: '',
})
const emailError = ref('')
const emailSaving = ref(false)

async function loadUsers() {
  loading.value = true
  try {
    const response = await apiClient.get('/users')
    users.value = response.data
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler beim Laden', life: 5000 })
  } finally {
    loading.value = false
  }
}

async function loadSettings() {
  try {
    const res = await apiClient.get('/settings')
    publicRegistration.value = res.data.public_registration
    smtpForm.value.system_smtp_host = res.data.system_smtp_host || ''
    smtpForm.value.system_smtp_port = res.data.system_smtp_port || 587
    smtpForm.value.system_smtp_ssl = res.data.system_smtp_ssl
    smtpForm.value.system_smtp_username = res.data.system_smtp_username || ''
    smtpForm.value.system_email_from = res.data.system_email_from || ''
    smtpPasswordSet.value = res.data.system_smtp_password_set
    onlyofficeConfigured.value = res.data.onlyoffice_configured
    onlyofficeUrl.value = res.data.onlyoffice_url || ''
  } catch { /* first load, defaults */ }
}

async function saveSettings() {
  try {
    await apiClient.put('/settings', { public_registration: publicRegistration.value })
    toast.add({ severity: 'success', summary: 'Einstellungen gespeichert', life: 2000 })
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
  }
}

// --- SFTP Targets ---
async function loadTargets() {
  try {
    const res = await apiClient.get('/admin/backup/targets')
    sftpTargets.value = res.data
  } catch { sftpTargets.value = [] }
}

function intervalLabel(minutes) {
  if (!minutes) return 'Deaktiviert'
  if (minutes < 60) return `Alle ${minutes} Min.`
  if (minutes < 1440) return `Alle ${minutes / 60} Std.`
  if (minutes === 1440) return 'Taeglich'
  if (minutes === 10080) return 'Woechentlich'
  return `Alle ${Math.round(minutes / 1440)} Tage`
}

function formatDateTime(iso) {
  if (!iso) return ''
  return new Date(iso).toLocaleString('de-DE', {
    day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit',
  })
}

function openNewTarget() {
  editingTarget.value = null
  targetForm.value = {
    name: '', host: '', port: 22, username: '', password: '',
    remote_path: '/backups/minicloud', is_active: true,
    backup_interval_minutes: 1440, max_versions: 10,
  }
  targetError.value = ''
  showTargetDialog.value = true
}

function openEditTarget(tgt) {
  editingTarget.value = tgt
  targetForm.value = {
    name: tgt.name, host: tgt.host, port: tgt.port, username: tgt.username, password: '',
    remote_path: tgt.remote_path, is_active: tgt.is_active,
    backup_interval_minutes: tgt.backup_interval_minutes, max_versions: tgt.max_versions,
  }
  targetError.value = ''
  showTargetDialog.value = true
}

async function saveTarget() {
  targetError.value = ''
  targetSaving.value = true
  try {
    const payload = { ...targetForm.value }
    if (editingTarget.value) {
      if (!payload.password) delete payload.password
      await apiClient.put(`/admin/backup/targets/${editingTarget.value.id}`, payload)
    } else {
      if (!payload.password) { targetError.value = 'Passwort erforderlich'; return }
      await apiClient.post('/admin/backup/targets', payload)
    }
    showTargetDialog.value = false
    await loadTargets()
    toast.add({ severity: 'success', summary: 'Backup-Ziel gespeichert', life: 3000 })
  } catch (err) {
    targetError.value = err.response?.data?.error || 'Fehler'
  } finally {
    targetSaving.value = false
  }
}

function confirmDeleteSftp(tgt) {
  deleteSftpTarget_ref.value = tgt
  showDeleteSftpConfirm.value = true
}

async function doDeleteSftpTarget() {
  if (!deleteSftpTarget_ref.value) return
  try {
    await apiClient.delete(`/admin/backup/targets/${deleteSftpTarget_ref.value.id}`)
    showDeleteSftpConfirm.value = false
    await loadTargets()
    toast.add({ severity: 'success', summary: 'Backup-Ziel geloescht', life: 3000 })
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
  }
}

async function testTarget(tgt) {
  try {
    const res = await apiClient.post(`/admin/backup/targets/${tgt.id}/test`)
    toast.add({ severity: 'success', summary: res.data.message, life: 5000 })
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Verbindung fehlgeschlagen', detail: err.response?.data?.error, life: 8000 })
  }
}

async function runBackupNow(tgt) {
  tgt._running = true
  try {
    const res = await apiClient.post(`/admin/backup/targets/${tgt.id}/run`, {}, { timeout: 600000 })
    toast.add({ severity: 'success', summary: res.data.message, life: 5000 })
    await loadTargets()
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Backup fehlgeschlagen', detail: err.response?.data?.error, life: 8000 })
  } finally {
    tgt._running = false
  }
}

async function openVersions(tgt) {
  versionsTarget.value = tgt
  versions.value = []
  versionsLoading.value = true
  showVersionsDialog.value = true
  try {
    const res = await apiClient.get(`/admin/backup/targets/${tgt.id}/versions`)
    versions.value = res.data
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
  } finally {
    versionsLoading.value = false
  }
}

async function restoreFromVersion(ver) {
  if (!versionsTarget.value) return
  ver._restoring = true
  try {
    const res = await apiClient.post(
      `/admin/backup/targets/${versionsTarget.value.id}/restore/${ver.name}`,
      {}, { timeout: 600000 }
    )
    toast.add({ severity: 'success', summary: 'Restore erfolgreich', detail: res.data.message, life: 5000 })
    showVersionsDialog.value = false
    await loadUsers()
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Restore fehlgeschlagen', detail: err.response?.data?.error, life: 8000 })
  } finally {
    ver._restoring = false
  }
}

// --- Backup File Browser ---
function browseFileIcon(path) {
  if (path === 'metadata.json') return 'pi pi-info-circle'
  if (path === 'database.sqlite3') return 'pi pi-database'
  if (path.startsWith('files/')) return 'pi pi-file'
  return 'pi pi-file'
}

async function browseLocalBackup() {
  if (!restoreFile.value) return
  browseLoading.value = true
  showFileBrowser.value = true
  browseFilter.value = ''
  try {
    const formData = new FormData()
    formData.append('file', restoreFile.value)
    const res = await apiClient.post('/admin/restore/browse', formData, {
      headers: { 'Content-Type': 'multipart/form-data' }, timeout: 300000,
    })
    browseId.value = res.data.browse_id
    browseFiles.value = res.data.files
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
    showFileBrowser.value = false
  } finally {
    browseLoading.value = false
  }
}

async function browseSftpVersion(ver) {
  if (!versionsTarget.value) return
  ver._browsing = true
  browseLoading.value = true
  showFileBrowser.value = true
  browseFilter.value = ''
  try {
    const res = await apiClient.post(
      `/admin/backup/targets/${versionsTarget.value.id}/versions/${ver.name}/browse`,
      {}, { timeout: 300000 }
    )
    browseId.value = res.data.browse_id
    browseFiles.value = res.data.files
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
    showFileBrowser.value = false
  } finally {
    browseLoading.value = false
    ver._browsing = false
  }
}

function downloadFromBrowse(file) {
  window.open(`/api/admin/restore/browse/${browseId.value}/download/${file.path}`, '_blank')
}

async function restoreSingleFile(file) {
  try {
    const res = await apiClient.post(`/admin/restore/browse/${browseId.value}/restore-file`, {
      file_path: file.path,
    })
    toast.add({ severity: 'success', summary: 'Datei wiederhergestellt', detail: res.data.message, life: 5000 })
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
  }
}

async function closeBrowseSession() {
  if (browseId.value) {
    try { await apiClient.post(`/admin/restore/browse/${browseId.value}/close`) } catch {}
    browseId.value = ''
    browseFiles.value = []
  }
}

// --- Backup & Restore (Local) ---
function formatSize(bytes) {
  if (!bytes) return '0 B'
  const units = ['B', 'KB', 'MB', 'GB', 'TB']
  let i = 0; let size = bytes
  while (size >= 1024 && i < units.length - 1) { size /= 1024; i++ }
  return `${size.toFixed(i > 0 ? 1 : 0)} ${units[i]}`
}

async function createBackup() {
  backupLoading.value = true
  try {
    const response = await apiClient.post('/admin/backup', {}, { responseType: 'blob' })
    const url = URL.createObjectURL(response.data)
    const a = document.createElement('a')
    a.href = url
    const disposition = response.headers['content-disposition'] || ''
    const match = disposition.match(/filename="?(.+?)"?$/)
    a.download = match ? match[1] : `minicloud_backup_${new Date().toISOString().slice(0,10)}.zip`
    a.click()
    URL.revokeObjectURL(url)
    toast.add({ severity: 'success', summary: 'Backup heruntergeladen', life: 3000 })
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Backup fehlgeschlagen', detail: err.response?.data?.error, life: 5000 })
  } finally {
    backupLoading.value = false
  }
}

function onRestoreFileSelected(event) {
  restoreFile.value = event.target.files[0] || null
  restoreResult.value = null
}

async function startRestore() {
  if (!restoreFile.value) return

  restoreInProgress.value = true
  restoreProgress.value = 0
  restoreStatus.value = 'Starte Upload...'
  restoreResult.value = null

  const file = restoreFile.value
  const totalChunks = Math.ceil(file.size / CHUNK_SIZE)

  try {
    if (file.size <= 100 * 1024 * 1024) {
      // Small file: direct upload
      restoreStatus.value = 'Lade Datei hoch...'
      restoreProgress.value = 50
      const formData = new FormData()
      formData.append('file', file)
      const res = await apiClient.post('/admin/restore/direct', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
        timeout: 600000,
      })
      restoreProgress.value = 100
      restoreResult.value = res.data
    } else {
      // Large file: chunked upload
      // 1. Init
      restoreStatus.value = 'Initialisiere Upload...'
      const initRes = await apiClient.post('/admin/restore/init', {
        total_size: file.size,
        total_chunks: totalChunks,
        filename: file.name,
      })
      const uploadId = initRes.data.upload_id

      // 2. Upload chunks
      for (let i = 0; i < totalChunks; i++) {
        const start = i * CHUNK_SIZE
        const end = Math.min(start + CHUNK_SIZE, file.size)
        const chunk = file.slice(start, end)

        const formData = new FormData()
        formData.append('upload_id', uploadId)
        formData.append('chunk_number', i.toString())
        formData.append('chunk', chunk)

        await apiClient.post('/admin/restore/chunk', formData, {
          headers: { 'Content-Type': 'multipart/form-data' },
          timeout: 120000,
        })

        restoreProgress.value = Math.round(((i + 1) / totalChunks) * 80)
        restoreStatus.value = `Chunk ${i + 1} / ${totalChunks} hochgeladen (${formatSize(end)} / ${formatSize(file.size)})`
      }

      // 3. Finalize
      restoreStatus.value = 'Stelle Daten wieder her...'
      restoreProgress.value = 85
      const finalRes = await apiClient.post('/admin/restore/finalize', {
        upload_id: uploadId,
      }, { timeout: 600000 })

      restoreProgress.value = 100
      restoreResult.value = finalRes.data
    }

    restoreStatus.value = 'Fertig!'
    if (restoreResult.value?.success) {
      toast.add({ severity: 'success', summary: 'Restore erfolgreich', life: 5000 })
      await loadUsers()
    }
  } catch (err) {
    restoreResult.value = {
      success: false,
      message: err.response?.data?.error || 'Restore fehlgeschlagen: ' + String(err),
    }
    toast.add({ severity: 'error', summary: 'Restore fehlgeschlagen', life: 5000 })
  } finally {
    restoreInProgress.value = false
    restoreFile.value = null
    if (restoreFileInput.value) restoreFileInput.value.value = ''
  }
}

// --- Invite links ---
async function createInvite() {
  inviteLoading.value = true
  inviteResult.value = null
  try {
    const payload = {}
    if (inviteEmail.value.trim()) payload.send_to_email = inviteEmail.value.trim()
    const res = await apiClient.post('/settings/invite', payload)
    inviteResult.value = res.data
    toast.add({ severity: 'success', summary: 'Einladungslink erstellt', life: 3000 })
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
  } finally {
    inviteLoading.value = false
  }
}

function copyInviteLink() {
  navigator.clipboard.writeText(fullInviteUrl.value)
  toast.add({ severity: 'info', summary: 'Link kopiert', life: 2000 })
}

// --- System SMTP ---
async function saveSmtp() {
  try {
    await apiClient.put('/settings', smtpForm.value)
    toast.add({ severity: 'success', summary: 'SMTP-Einstellungen gespeichert', life: 3000 })
    await loadSettings()
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
  }
}

async function testSmtp() {
  smtpTesting.value = true
  try {
    // Save first, then test
    await apiClient.put('/settings', smtpForm.value)
    const res = await apiClient.post('/settings/test-email')
    toast.add({ severity: 'success', summary: res.data.message, life: 5000 })
  } catch (err) {
    toast.add({ severity: 'error', summary: 'SMTP-Test fehlgeschlagen', detail: err.response?.data?.error, life: 8000 })
  } finally {
    smtpTesting.value = false
  }
}

// --- Users ---
function openNewUser() {
  editingUser.value = null
  userEmailAccounts.value = []
  userForm.value = { username: '', email: '', password: '', role: 'user', storage_quota_mb: 5120, is_active: true }
  userError.value = ''
  showUserDialog.value = true
}

async function openEditUser(user) {
  editingUser.value = user
  userForm.value = {
    username: user.username,
    email: user.email || '',
    password: '',
    role: user.role,
    storage_quota_mb: user.storage_quota_mb,
    is_active: user.is_active,
  }
  userError.value = ''
  showUserDialog.value = true
  // Load email accounts for this user
  try {
    const res = await apiClient.get(`/admin/users/${user.id}/email-accounts`)
    userEmailAccounts.value = res.data
  } catch { userEmailAccounts.value = [] }
}

// --- Email accounts for user ---
function openNewEmailAccount() {
  editingEmailAcc.value = null
  emailForm.value = {
    display_name: '', email_address: '', imap_host: '', imap_port: 993, imap_ssl: true,
    smtp_host: '', smtp_port: 587, smtp_ssl: true, username: '', password: '',
  }
  emailError.value = ''
  showEmailDialog.value = true
}

function openEditEmailAccount(acc) {
  editingEmailAcc.value = acc
  emailForm.value = {
    display_name: acc.display_name, email_address: acc.email_address,
    imap_host: acc.imap_host, imap_port: acc.imap_port, imap_ssl: acc.imap_ssl,
    smtp_host: acc.smtp_host, smtp_port: acc.smtp_port, smtp_ssl: acc.smtp_ssl,
    username: acc.username, password: '',
  }
  emailError.value = ''
  showEmailDialog.value = true
}

async function saveEmailAccount() {
  emailError.value = ''
  emailSaving.value = true
  try {
    const payload = { ...emailForm.value }
    const headers = { 'X-Encryption-Key': auth.masterKeySalt || 'admin-key' }

    if (editingEmailAcc.value) {
      if (!payload.password) delete payload.password
      await apiClient.put(`/admin/email-accounts/${editingEmailAcc.value.id}`, payload, { headers })
    } else {
      if (!payload.password) { emailError.value = 'Passwort erforderlich'; return }
      await apiClient.post(`/admin/users/${editingUser.value.id}/email-accounts`, payload, { headers })
    }
    showEmailDialog.value = false
    const res = await apiClient.get(`/admin/users/${editingUser.value.id}/email-accounts`)
    userEmailAccounts.value = res.data
    toast.add({ severity: 'success', summary: 'E-Mail-Konto gespeichert', life: 3000 })
  } catch (err) {
    emailError.value = err.response?.data?.error || 'Fehler'
  } finally {
    emailSaving.value = false
  }
}

function confirmDeleteEmail(acc) {
  deleteEmailTarget.value = acc
  showDeleteEmailConfirm.value = true
}

async function doDeleteEmailTarget() {
  if (!deleteEmailTarget.value) return
  try {
    await apiClient.delete(`/admin/email-accounts/${deleteEmailTarget.value.id}`)
    userEmailAccounts.value = userEmailAccounts.value.filter(a => a.id !== deleteEmailTarget.value.id)
    showDeleteEmailConfirm.value = false
    toast.add({ severity: 'success', summary: 'Konto geloescht', life: 3000 })
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
  }
}

async function saveUser() {
  userError.value = ''
  userSaving.value = true
  try {
    if (editingUser.value) {
      const payload = { ...userForm.value }
      if (!payload.password) delete payload.password
      delete payload.username
      await apiClient.put(`/users/${editingUser.value.id}`, payload)
      toast.add({ severity: 'success', summary: 'Benutzer aktualisiert', life: 3000 })
    } else {
      if (!userForm.value.password) {
        userError.value = 'Passwort erforderlich'
        return
      }
      await apiClient.post('/users', userForm.value)
      toast.add({ severity: 'success', summary: 'Benutzer erstellt', life: 3000 })
    }
    showUserDialog.value = false
    await loadUsers()
  } catch (err) {
    userError.value = err.response?.data?.error || 'Fehler beim Speichern'
  } finally {
    userSaving.value = false
  }
}

function confirmDeleteUser(user) {
  deleteTarget.value = user
  showDeleteConfirm.value = true
}

async function doDeleteUser() {
  if (!deleteTarget.value) return
  try {
    await apiClient.delete(`/users/${deleteTarget.value.id}`)
    showDeleteConfirm.value = false
    toast.add({ severity: 'success', summary: 'Benutzer geloescht', life: 3000 })
    await loadUsers()
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
  }
}

onMounted(() => {
  loadUsers()
  loadSettings()
  loadTargets()
})
</script>

<style scoped>
.view-container { padding: 1.5rem; }
.view-header h2 { margin: 0 0 1.5rem; }
.admin-section {
  background: var(--p-surface-0); border-radius: 8px; padding: 1.5rem; margin-bottom: 1.5rem;
}
.admin-section h3 { margin: 0 0 1rem; font-size: 1.125rem; }
.section-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; }
.section-header h3 { margin: 0; }
.setting-row {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.75rem 0;
}
.setting-info { flex: 1; }
.setting-info strong { display: block; margin-bottom: 0.25rem; }
.setting-info p { margin: 0; font-size: 0.85rem; color: var(--p-text-muted-color); }
.field { margin-bottom: 1rem; }
.field label { display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem; }
.field-row { display: flex; gap: 0.75rem; align-items: flex-end; }
.flex-grow { flex: 1; }
.hint { font-size: 0.85rem; color: var(--p-text-muted-color); margin: 0 0 0.75rem; }
.invite-section { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--p-surface-200); }
.invite-section h4 { margin: 0 0 0.25rem; font-size: 0.95rem; }
.invite-row { display: flex; gap: 0.5rem; align-items: flex-start; }
.invite-result { margin-top: 0.75rem; }
.invite-url { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
.invite-url code { font-size: 0.8rem; word-break: break-all; background: var(--p-surface-100); padding: 0.375rem 0.5rem; border-radius: 4px; }
.smtp-form { max-width: 550px; }
.smtp-actions { display: flex; gap: 0.5rem; }
.header-actions { display: flex; gap: 0.5rem; align-items: center; }
.search-input { width: 220px; }
.email-tab { }
.email-account-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.5rem 0; border-bottom: 1px solid var(--p-surface-100);
}
.acc-info { display: flex; flex-direction: column; gap: 0.125rem; }
.acc-info span { font-size: 0.825rem; color: var(--p-text-muted-color); }
.acc-actions { display: flex; }
.empty-hint-small { padding: 1rem; color: var(--p-text-muted-color); font-size: 0.875rem; text-align: center; }
.section-title { margin: 1rem 0 0.5rem; font-size: 0.95rem; font-weight: 600; }
.backup-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
@media (max-width: 900px) { .backup-grid { grid-template-columns: 1fr; } }
.backup-card { border: 1px solid var(--p-surface-200); border-radius: 8px; padding: 1.25rem; }
.backup-card h4 { margin: 0 0 0.75rem; display: flex; align-items: center; gap: 0.5rem; }
.backup-card p { font-size: 0.875rem; color: var(--p-text-muted-color); margin: 0 0 1rem; }
.restore-instructions { background: var(--p-surface-50); border-radius: 6px; padding: 1rem; margin-bottom: 1rem; font-size: 0.85rem; }
.restore-instructions ol { margin: 0.5rem 0 0; padding-left: 1.25rem; }
.restore-instructions li { margin-bottom: 0.375rem; line-height: 1.4; }
.restore-instructions code { background: var(--p-surface-200); padding: 0.125rem 0.375rem; border-radius: 3px; font-size: 0.8rem; }
.restore-info { margin-top: 1rem; }
.restore-info p { margin-bottom: 0.75rem; }
.restore-progress { margin-top: 1rem; }
.restore-progress p { margin: 0.5rem 0; }
.progress-text { font-size: 0.825rem; color: var(--p-text-muted-color); }
.restore-result { margin-top: 1rem; }
.result-details { font-size: 0.85rem; margin-top: 0.5rem; }
.result-details ul { margin: 0.25rem 0; padding-left: 1.25rem; }
.sftp-target-card {
  border: 1px solid var(--p-surface-200); border-radius: 8px; padding: 1rem; margin-bottom: 0.75rem;
}
.target-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }
.target-info strong { display: block; }
.target-detail { font-size: 0.8rem; color: var(--p-text-muted-color); font-family: monospace; }
.target-status { display: flex; gap: 0.375rem; flex-shrink: 0; }
.target-last-backup { font-size: 0.825rem; margin: 0.5rem 0; display: flex; align-items: center; gap: 0.375rem; }
.backup-msg { color: var(--p-text-muted-color); }
.target-actions { display: flex; gap: 0.25rem; flex-wrap: wrap; margin-top: 0.5rem; }
.version-item {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.625rem 0; border-bottom: 1px solid var(--p-surface-100);
}
.version-info { display: flex; flex-direction: column; gap: 0.125rem; }
.version-info span { font-size: 0.8rem; color: var(--p-text-muted-color); }
.loading-center { text-align: center; padding: 2rem; color: var(--p-text-muted-color); }
.restore-buttons { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
.version-actions { display: flex; gap: 0.25rem; }
.browse-toolbar { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.75rem; }
.browse-count { font-size: 0.8rem; color: var(--p-text-muted-color); white-space: nowrap; }
.browse-file-list { max-height: 450px; overflow-y: auto; }
.browse-file-item {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.5rem 0; border-bottom: 1px solid var(--p-surface-100);
}
.browse-file-info { display: flex; align-items: center; gap: 0.5rem; flex: 1; min-width: 0; }
.browse-file-detail { display: flex; flex-direction: column; min-width: 0; }
.browse-file-path { font-size: 0.825rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: monospace; }
.browse-file-meta { font-size: 0.75rem; color: var(--p-text-muted-color); }
.browse-file-actions { display: flex; gap: 0; flex-shrink: 0; }
</style>
