<template>
  <div class="view-container">
    <div class="view-header">
      <h2>Passwoerter</h2>
      <div class="header-actions">
        <Button icon="pi pi-folder-plus" label="Neuer Ordner" size="small" outlined @click="showNewFolder = true" />
        <Button icon="pi pi-plus" label="Neuer Eintrag" size="small" @click="openNewEntry" />
        <Button icon="pi pi-upload" label="Import" size="small" outlined @click="showImport = true" />
      </div>
    </div>

    <div class="passwords-layout">
      <aside class="folders-sidebar">
        <div class="folder-item" :class="{ active: selectedFolderId === null }" @click="selectedFolderId = null; loadEntries()">
          <i class="pi pi-key"></i>
          <span>Alle</span>
        </div>
        <div v-for="folder in folders" :key="folder.id"
             class="folder-item" :class="{ active: selectedFolderId === folder.id }"
             @click="selectedFolderId = folder.id; loadEntries()">
          <i :class="folder.icon || 'pi pi-folder'"></i>
          <span>{{ folder.name }}</span>
          <span v-if="folder.owner_name" class="shared-label">({{ folder.owner_name }})</span>
        </div>
      </aside>

      <div class="entries-main">
        <div class="search-bar">
          <InputText v-model="searchQuery" placeholder="Passwoerter suchen..." fluid />
        </div>

        <div class="entries-list">
          <div v-for="entry in filteredEntries" :key="entry.id"
               class="entry-item" @click="openEntry(entry)">
            <div class="entry-icon">
              <i class="pi pi-key"></i>
            </div>
            <div class="entry-info">
              <div class="entry-title">{{ decryptedEntries[entry.id]?.title || '(Verschluesselt)' }}</div>
              <div class="entry-url">{{ decryptedEntries[entry.id]?.username || '' }}</div>
            </div>
            <div class="entry-actions">
              <Button icon="pi pi-copy" text size="small" title="Passwort kopieren"
                      @click.stop="copyPassword(entry)" />
              <Button v-if="decryptedEntries[entry.id]?.totp_secret" icon="pi pi-clock" text size="small"
                      title="TOTP Code" @click.stop="showTotp(entry)" />
            </div>
          </div>
          <div v-if="!filteredEntries.length" class="empty">
            <i class="pi pi-key" style="font-size: 2rem; color: var(--p-surface-400)"></i>
            <p>Keine Eintraege</p>
          </div>
        </div>
      </div>
    </div>

    <!-- Entry Dialog -->
    <Dialog v-model:visible="showEntryDialog" :header="editingEntry ? 'Eintrag bearbeiten' : 'Neuer Eintrag'" modal :style="{ width: '500px' }">
      <div class="field">
        <label>Titel</label>
        <InputText v-model="entryForm.title" fluid autofocus />
      </div>
      <div class="field">
        <label>URL</label>
        <InputText v-model="entryForm.url" fluid />
      </div>
      <div class="field">
        <label>Benutzername</label>
        <InputText v-model="entryForm.username" fluid />
      </div>
      <div class="field">
        <label>Passwort</label>
        <div class="password-field">
          <Password v-model="entryForm.password" :feedback="false" toggle-mask fluid />
          <Button icon="pi pi-sync" text size="small" title="Generieren" @click="generatePassword" />
        </div>
      </div>
      <div class="field">
        <label>TOTP Secret (optional)</label>
        <InputText v-model="entryForm.totp_secret" fluid placeholder="otpauth:// oder Secret" />
      </div>
      <div class="field">
        <label>Ordner</label>
        <Select v-model="entryForm.folder_id" :options="folderOptions" optionLabel="name" optionValue="id" fluid placeholder="Kein Ordner" />
      </div>
      <div class="field">
        <label>Kategorie</label>
        <InputText v-model="entryForm.category" fluid />
      </div>
      <div class="field">
        <label>Notizen</label>
        <Textarea v-model="entryForm.notes" rows="3" fluid />
      </div>
      <template #footer>
        <Button v-if="editingEntry" label="Loeschen" severity="danger" text @click="deleteEntry" />
        <Button label="Abbrechen" text @click="showEntryDialog = false" />
        <Button :label="editingEntry ? 'Speichern' : 'Erstellen'" @click="saveEntry" />
      </template>
    </Dialog>

    <!-- New Folder -->
    <Dialog v-model:visible="showNewFolder" header="Neuer Ordner" modal :style="{ width: '400px' }">
      <div class="field">
        <label>Name</label>
        <InputText v-model="newFolderName" fluid autofocus @keyup.enter="createFolder" />
      </div>
      <template #footer>
        <Button label="Abbrechen" text @click="showNewFolder = false" />
        <Button label="Erstellen" @click="createFolder" />
      </template>
    </Dialog>

    <!-- Import Dialog -->
    <Dialog v-model:visible="showImport" header="Passwoerter importieren" modal :style="{ width: '520px' }">
      <div class="field">
        <label>Quelle</label>
        <Select v-model="importSource" :options="importSources" optionLabel="label" optionValue="value" fluid />
      </div>

      <div class="import-hint">
        <template v-if="importSource === 'keepass'">
          <p>Waehle eine <strong>.kdbx</strong>-Datei und gib das KeePass-Master-Passwort ein.</p>
        </template>
        <template v-else-if="importSource === 'firefox'">
          <p>Firefox: <strong>Einstellungen > Passwoerter > ⋯ > Passwoerter exportieren</strong><br/>Waehle die exportierte CSV-Datei aus.</p>
        </template>
        <template v-else-if="importSource === 'csv'">
          <p>CSV mit Spalten wie <em>title/name, url, username, password, notes</em>.<br/>Funktioniert mit Chrome, Bitwarden, 1Password und anderen Managern.</p>
        </template>
      </div>

      <div class="field">
        <label>Datei</label>
        <input ref="importFileInput" type="file" :accept="importAccept" />
      </div>

      <div v-if="importSource === 'keepass'" class="field">
        <label>KeePass-Passwort</label>
        <Password v-model="importPassword" :feedback="false" toggle-mask fluid />
      </div>

      <template #footer>
        <Button label="Abbrechen" text @click="showImport = false" />
        <Button label="Importieren" @click="doImport" :loading="importing" />
      </template>
    </Dialog>

    <!-- TOTP Dialog -->
    <Dialog v-model:visible="showTotpDialog" header="TOTP Code" modal :style="{ width: '300px' }">
      <div class="totp-display">
        <div class="totp-code">{{ totpCode }}</div>
        <Button icon="pi pi-copy" text @click="copyToClipboard(totpCode)" />
      </div>
    </Dialog>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue'
import { useToast } from 'primevue/usetoast'
import { useAuthStore } from '../stores/auth'
import apiClient from '../api/client'
import Button from 'primevue/button'
import Dialog from 'primevue/dialog'
import InputText from 'primevue/inputtext'
import Password from 'primevue/password'
import Textarea from 'primevue/textarea'
import Select from 'primevue/select'

const toast = useToast()
const auth = useAuthStore()

const folders = ref([])
const entries = ref([])
const decryptedEntries = ref({})
const selectedFolderId = ref(null)
const searchQuery = ref('')

const showEntryDialog = ref(false)
const editingEntry = ref(null)
const entryForm = ref({ title: '', url: '', username: '', password: '', totp_secret: '', folder_id: null, category: '', notes: '' })

const showNewFolder = ref(false)
const newFolderName = ref('')
const showImport = ref(false)
const importSource = ref('firefox')
const importPassword = ref('')
const importing = ref(false)
const importFileInput = ref(null)
const importSources = [
  { label: 'Firefox (CSV)', value: 'firefox' },
  { label: 'KeePass (.kdbx)', value: 'keepass' },
  { label: 'CSV (Chrome, Bitwarden, etc.)', value: 'csv' },
]
const importAccept = computed(() => {
  if (importSource.value === 'keepass') return '.kdbx'
  return '.csv'
})

const showTotpDialog = ref(false)
const totpCode = ref('')

const folderOptions = computed(() => [{ id: null, name: '(Kein Ordner)' }, ...folders.value])
const filteredEntries = computed(() => {
  if (!searchQuery.value) return entries.value
  const q = searchQuery.value.toLowerCase()
  return entries.value.filter(e => {
    const d = decryptedEntries.value[e.id]
    if (!d) return false
    return (d.title || '').toLowerCase().includes(q) ||
           (d.username || '').toLowerCase().includes(q) ||
           (d.url || '').toLowerCase().includes(q)
  })
})

// --- Crypto helpers using Web Crypto API ---
async function getMasterKey() {
  const salt = auth.masterKeySalt
  if (!salt) return null
  const enc = new TextEncoder()
  const keyMaterial = await crypto.subtle.importKey('raw', enc.encode(auth.user?.username + ':' + 'stored'), 'PBKDF2', false, ['deriveKey'])
  return crypto.subtle.deriveKey(
    { name: 'PBKDF2', salt: Uint8Array.from(atob(salt), c => c.charCodeAt(0)), iterations: 600000, hash: 'SHA-256' },
    keyMaterial, { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']
  )
}

async function encryptText(text, key) {
  if (!text) return null
  const iv = crypto.getRandomValues(new Uint8Array(12))
  const enc = new TextEncoder()
  const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, enc.encode(text))
  const combined = new Uint8Array(iv.length + ciphertext.byteLength)
  combined.set(iv)
  combined.set(new Uint8Array(ciphertext), iv.length)
  return btoa(String.fromCharCode(...combined))
}

async function decryptText(b64, key) {
  if (!b64) return ''
  try {
    const raw = Uint8Array.from(atob(b64), c => c.charCodeAt(0))
    const iv = raw.slice(0, 12)
    const ciphertext = raw.slice(12)
    const plaintext = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ciphertext)
    return new TextDecoder().decode(plaintext)
  } catch { return '(Entschluesselung fehlgeschlagen)' }
}

async function decryptEntries() {
  const key = await getMasterKey()
  if (!key) return
  const result = {}
  for (const entry of entries.value) {
    result[entry.id] = {
      title: await decryptText(entry.title_encrypted, key),
      url: await decryptText(entry.url_encrypted, key),
      username: await decryptText(entry.username_encrypted, key),
      password: await decryptText(entry.password_encrypted, key),
      notes: await decryptText(entry.notes_encrypted, key),
      totp_secret: await decryptText(entry.totp_secret_encrypted, key),
    }
  }
  decryptedEntries.value = result
}

// --- Data loading ---
async function loadFolders() {
  const res = await apiClient.get('/passwords/folders')
  folders.value = res.data
}

async function loadEntries() {
  const params = {}
  if (selectedFolderId.value !== null) params.folder_id = selectedFolderId.value
  const res = await apiClient.get('/passwords/entries', { params })
  entries.value = res.data
  await decryptEntries()
}

async function createFolder() {
  if (!newFolderName.value.trim()) return
  await apiClient.post('/passwords/folders', { name: newFolderName.value.trim() })
  showNewFolder.value = false
  newFolderName.value = ''
  await loadFolders()
}

function openNewEntry() {
  editingEntry.value = null
  entryForm.value = { title: '', url: '', username: '', password: '', totp_secret: '', folder_id: selectedFolderId.value, category: '', notes: '' }
  showEntryDialog.value = true
}

async function openEntry(entry) {
  editingEntry.value = entry
  const d = decryptedEntries.value[entry.id] || {}
  entryForm.value = {
    title: d.title || '',
    url: d.url || '',
    username: d.username || '',
    password: d.password || '',
    totp_secret: d.totp_secret || '',
    folder_id: entry.folder_id,
    category: entry.category || '',
    notes: d.notes || '',
  }
  showEntryDialog.value = true
}

async function saveEntry() {
  const key = await getMasterKey()
  if (!key) { toast.add({ severity: 'error', summary: 'Kein Master-Key', life: 3000 }); return }

  const iv = crypto.getRandomValues(new Uint8Array(12))
  const ivB64 = btoa(String.fromCharCode(...iv))

  const payload = {
    title_encrypted: await encryptText(entryForm.value.title, key),
    url_encrypted: await encryptText(entryForm.value.url, key),
    username_encrypted: await encryptText(entryForm.value.username, key),
    password_encrypted: await encryptText(entryForm.value.password, key),
    notes_encrypted: await encryptText(entryForm.value.notes, key),
    totp_secret_encrypted: await encryptText(entryForm.value.totp_secret, key),
    iv: ivB64,
    folder_id: entryForm.value.folder_id,
    category: entryForm.value.category,
  }

  if (editingEntry.value) {
    await apiClient.put(`/passwords/entries/${editingEntry.value.id}`, payload)
  } else {
    await apiClient.post('/passwords/entries', payload)
  }
  showEntryDialog.value = false
  await loadEntries()
}

async function deleteEntry() {
  if (!editingEntry.value) return
  await apiClient.delete(`/passwords/entries/${editingEntry.value.id}`)
  showEntryDialog.value = false
  await loadEntries()
}

async function copyPassword(entry) {
  const d = decryptedEntries.value[entry.id]
  if (d?.password) {
    await navigator.clipboard.writeText(d.password)
    toast.add({ severity: 'info', summary: 'Passwort kopiert', life: 2000 })
  }
}

function copyToClipboard(text) {
  navigator.clipboard.writeText(text)
  toast.add({ severity: 'info', summary: 'Kopiert', life: 2000 })
}

function generatePassword() {
  const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-='
  const arr = new Uint8Array(20)
  crypto.getRandomValues(arr)
  entryForm.value.password = Array.from(arr, b => chars[b % chars.length]).join('')
}

async function showTotp(entry) {
  const d = decryptedEntries.value[entry.id]
  if (!d?.totp_secret) return
  // Simple TOTP generation
  try {
    const secret = d.totp_secret.replace(/^otpauth:\/\/.*secret=/, '').replace(/&.*/, '')
    totpCode.value = await generateTOTP(secret)
    showTotpDialog.value = true
  } catch {
    toast.add({ severity: 'error', summary: 'TOTP-Fehler', life: 3000 })
  }
}

async function generateTOTP(secret) {
  // Base32 decode
  const base32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
  const bits = secret.toUpperCase().replace(/=+$/, '').split('').map(c => {
    const val = base32.indexOf(c)
    return val >= 0 ? val.toString(2).padStart(5, '0') : ''
  }).join('')
  const bytes = new Uint8Array(bits.match(/.{8}/g).map(b => parseInt(b, 2)))

  const time = Math.floor(Date.now() / 30000)
  const timeBytes = new Uint8Array(8)
  let t = time
  for (let i = 7; i >= 0; i--) { timeBytes[i] = t & 0xff; t >>= 8 }

  const key = await crypto.subtle.importKey('raw', bytes, { name: 'HMAC', hash: 'SHA-1' }, false, ['sign'])
  const sig = new Uint8Array(await crypto.subtle.sign('HMAC', key, timeBytes))
  const offset = sig[sig.length - 1] & 0xf
  const code = ((sig[offset] & 0x7f) << 24 | sig[offset + 1] << 16 | sig[offset + 2] << 8 | sig[offset + 3]) % 1000000
  return code.toString().padStart(6, '0')
}

async function doImport() {
  if (!importFileInput.value?.files?.length) return
  if (importSource.value === 'keepass' && !importPassword.value) {
    toast.add({ severity: 'warn', summary: 'KeePass-Passwort erforderlich', life: 3000 })
    return
  }

  importing.value = true
  try {
    // Step 1: Upload file to backend for parsing
    const formData = new FormData()
    formData.append('file', importFileInput.value.files[0])
    if (importSource.value === 'keepass') {
      formData.append('password', importPassword.value)
    }

    const endpoint = {
      keepass: '/passwords/import/keepass',
      firefox: '/passwords/import/firefox',
      csv: '/passwords/import/csv',
    }[importSource.value]

    const res = await apiClient.post(endpoint, formData, {
      headers: { 'Content-Type': 'multipart/form-data' }
    })

    if (!res.data.count) {
      toast.add({ severity: 'warn', summary: 'Keine Eintraege gefunden', life: 3000 })
      return
    }

    // Step 2: Encrypt and store entries client-side
    const key = await getMasterKey()
    if (!key) return

    // Create folders
    const folderMap = {}
    for (const group of res.data.groups) {
      try {
        const folder = await apiClient.post('/passwords/folders', { name: group.name })
        folderMap[group.uuid || group.name] = folder.data.id
      } catch { /* folder may already exist */ }
    }

    // Import entries
    let imported = 0
    for (const entry of res.data.entries) {
      const folderId = folderMap[entry.group_uuid || entry.group] || null
      const iv = crypto.getRandomValues(new Uint8Array(12))
      try {
        await apiClient.post('/passwords/entries', {
          title_encrypted: await encryptText(entry.title, key),
          url_encrypted: await encryptText(entry.url, key),
          username_encrypted: await encryptText(entry.username, key),
          password_encrypted: await encryptText(entry.password, key),
          notes_encrypted: await encryptText(entry.notes, key),
          totp_secret_encrypted: await encryptText(entry.totp, key),
          iv: btoa(String.fromCharCode(...iv)),
          folder_id: folderId,
        })
        imported++
      } catch { /* skip duplicates */ }
    }

    showImport.value = false
    toast.add({ severity: 'success', summary: `${imported} Eintraege importiert`, life: 5000 })
    await loadFolders()
    await loadEntries()
  } catch (err) {
    toast.add({ severity: 'error', summary: 'Import-Fehler', detail: err.response?.data?.error || String(err), life: 5000 })
  } finally {
    importing.value = false
  }
}

onMounted(async () => {
  await loadFolders()
  await loadEntries()
})
</script>

<style scoped>
.view-container { padding: 1.5rem; }
.view-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.5rem; }
.view-header h2 { margin: 0; }
.header-actions { display: flex; gap: 0.5rem; }
.passwords-layout { display: flex; gap: 1rem; }
.folders-sidebar { width: 220px; flex-shrink: 0; }
.folder-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; border-radius: 6px; cursor: pointer; font-size: 0.875rem; }
.folder-item:hover { background: var(--p-surface-100); }
.folder-item.active { background: var(--p-primary-50); color: var(--p-primary-color); }
.shared-label { color: var(--p-text-muted-color); font-size: 0.75rem; }
.entries-main { flex: 1; }
.search-bar { margin-bottom: 1rem; }
.entries-list { display: flex; flex-direction: column; gap: 2px; }
.entry-item { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; background: var(--p-surface-0); border-radius: 6px; cursor: pointer; }
.entry-item:hover { background: var(--p-surface-100); }
.entry-icon { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; background: var(--p-primary-50); border-radius: 8px; color: var(--p-primary-color); }
.entry-info { flex: 1; }
.entry-title { font-weight: 500; font-size: 0.9rem; }
.entry-url { font-size: 0.8rem; color: var(--p-text-muted-color); }
.entry-actions { display: flex; gap: 0; }
.field { margin-bottom: 1rem; }
.field label { display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem; }
.password-field { display: flex; gap: 0.5rem; align-items: flex-start; }
.empty { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; padding: 3rem; color: var(--p-text-muted-color); }
.totp-display { display: flex; align-items: center; justify-content: center; gap: 1rem; padding: 1rem; }
.totp-code { font-size: 2rem; font-weight: 700; letter-spacing: 0.25em; font-family: monospace; }
.import-hint { background: var(--p-surface-50); border-radius: 6px; padding: 0.75rem 1rem; margin-bottom: 1rem; font-size: 0.85rem; }
.import-hint p { margin: 0; line-height: 1.5; }
</style>
