<template>
  <div class="flex flex-col gap-4">
    <PageHeader :title="t('settings.title')" />

    <!-- Préférences (client) -->
    <section class="card flex flex-col gap-3">
      <h2 class="flex items-center gap-2 text-sm font-semibold text-fg">
        <SlidersHorizontal :size="16" /> {{ t('settings.preferences') }}
      </h2>
      <p class="text-xs text-fg-subtle">{{ t('settings.preferencesHint') }}</p>

      <div class="flex items-center justify-between gap-3">
        <span class="text-sm text-fg-muted">{{ t('settings.language') }}</span>
        <LanguageSwitcher />
      </div>

      <div class="flex items-center justify-between gap-3 border-t border-border/80 pt-3">
        <span class="text-sm text-fg-muted">{{ t('theme.label') }}</span>
        <SegmentedControl :model-value="themeMode" :options="themeOptions" @update:model-value="onTheme" />
      </div>

      <div class="flex flex-col gap-2 border-t border-border/80 pt-3">
        <div class="flex items-center justify-between gap-3">
          <div class="min-w-0">
            <p class="text-sm text-fg-muted">{{ t('settings.notifications') }}</p>
            <p class="text-xs text-fg-subtle">
              {{ push.supported ? (push.enabled ? t('settings.notificationsEnabled') : t('settings.notificationsDisabled')) : t('settings.pushUnsupported') }}
            </p>
          </div>
          <BaseButton
            v-if="push.supported"
            :variant="push.enabled ? 'secondary' : 'primary'"
            size="sm"
            :icon="push.enabled ? BellOff : Bell"
            :loading="push.busy"
            @click="push.toggle()"
          >
            {{ push.enabled ? t('settings.disableNotifications') : t('settings.enableNotifications') }}
          </BaseButton>
        </div>
        <div v-if="push.enabled">
          <BaseButton variant="ghost" size="sm" :icon="Send" :loading="testing" @click="sendTest">
            {{ t('settings.testNotification') }}
          </BaseButton>
        </div>
      </div>
    </section>

    <!-- Accès & sécurité : tokens -->
    <section class="card flex flex-col gap-3">
      <h2 class="flex items-center gap-2 text-sm font-semibold text-fg">
        <Shield :size="16" /> {{ t('settings.security') }}
      </h2>
      <p class="text-xs text-fg-subtle">{{ t('settings.securityHint') }}</p>

      <!-- Création -->
      <form class="flex flex-wrap items-end gap-2" @submit.prevent="createToken">
        <label class="flex min-w-40 flex-1 flex-col gap-1">
          <span class="text-xs text-fg-muted">{{ t('settings.newTokenLabel') }}</span>
          <input v-model="newLabel" class="input" :placeholder="t('settings.newTokenPlaceholder')" maxlength="64" />
        </label>
        <BaseButton type="submit" variant="primary" :icon="Plus" :loading="creating" :disabled="!newLabel.trim()">
          {{ t('settings.createToken') }}
        </BaseButton>
      </form>

      <!-- Valeur en clair (une seule fois) -->
      <div v-if="createdToken" class="card-inset flex flex-col gap-2 border-accent/60 bg-accent/15">
        <p class="text-xs text-accent">{{ t('settings.copyTokenHint') }}</p>
        <div class="flex items-center gap-2">
          <code class="min-w-0 flex-1 truncate rounded bg-surface-0 px-2 py-1 font-mono text-xs text-fg">{{ createdToken.token }}</code>
          <BaseButton size="sm" :icon="copiedNew ? Check : Copy" @click="copy(createdToken.token, 'new')">
            {{ copiedNew ? t('settings.copied') : t('settings.copy') }}
          </BaseButton>
          <BaseButton size="sm" variant="ghost" icon-only :icon="X" :aria-label="t('common.close')" @click="createdToken = null" />
        </div>
      </div>

      <!-- Liste -->
      <ul class="flex flex-col gap-2">
        <li
          v-for="tok in tokens"
          :key="tok.id"
          class="card-inset flex flex-wrap items-center gap-x-3 gap-y-1"
        >
          <KeyRound :size="15" class="shrink-0 text-fg-subtle" />
          <div class="min-w-0 flex-1">
            <p class="flex items-center gap-2 truncate text-sm text-fg">
              {{ tok.label }}
              <span v-if="tok.current" class="badge bg-accent/15 text-accent">{{ t('settings.current') }}</span>
            </p>
            <p class="text-[11px] text-fg-subtle">
              {{ t('settings.created', { date: fmt(tok.createdAt) }) }} ·
              {{ tok.lastUsedAt ? t('settings.lastUsed', { date: fmt(tok.lastUsedAt) }) : t('settings.neverUsed') }}
            </p>
          </div>
          <BaseButton
            v-if="!tok.current"
            size="sm"
            :variant="confirmId === tok.id ? 'danger' : 'ghost'"
            :icon="Trash2"
            @click="revoke(tok)"
          >
            {{ confirmId === tok.id ? t('settings.confirmRevoke') : t('settings.revoke') }}
          </BaseButton>
        </li>
      </ul>
    </section>

    <!-- Découverte des dépôts -->
    <section class="card flex flex-col gap-3">
      <h2 class="flex items-center gap-2 text-sm font-semibold text-fg">
        <ScanSearch :size="16" /> {{ t('settings.discovery') }}
      </h2>
      <p class="text-xs text-fg-subtle">{{ t('settings.discoveryHint') }}</p>

      <div class="flex flex-col gap-2">
        <span class="text-xs text-fg-muted">{{ t('settings.scanRoots') }}</span>
        <p v-if="rootsDraft.length === 0" class="text-xs text-fg-subtle">{{ t('settings.scanRootsEmpty') }}</p>
        <ul v-else class="flex flex-col gap-1">
          <li v-for="(root, i) in rootsDraft" :key="root" class="card-inset flex items-center gap-2">
            <span class="min-w-0 flex-1 truncate font-mono text-xs text-fg" :title="root">{{ root }}</span>
            <BaseButton size="sm" variant="ghost" icon-only :icon="X" :aria-label="t('settings.removeRoot')" @click="rootsDraft.splice(i, 1)" />
          </li>
        </ul>
        <div>
          <BaseButton size="sm" :icon="FolderOpen" @click="showRootPicker = !showRootPicker">{{ t('settings.addRoot') }}</BaseButton>
        </div>
        <DirectoryPicker v-if="showRootPicker" mode="dir" @select="onAddRoot" @close="showRootPicker = false" />
      </div>

      <label class="flex flex-col gap-1">
        <span class="text-xs text-fg-muted">{{ t('settings.scanInterval') }}</span>
        <input v-model.number="intervalDraft" type="number" min="0" max="1440" class="input w-32" />
        <span class="text-xs text-fg-subtle">{{ t('settings.scanIntervalHint') }}</span>
      </label>

      <div>
        <BaseButton variant="primary" :loading="settings.saving" :disabled="!discoveryDirty" @click="saveDiscovery">
          {{ t('settings.save') }}
        </BaseButton>
      </div>
    </section>

    <!-- Claude CLI -->
    <section class="card flex flex-col gap-3">
      <h2 class="flex items-center gap-2 text-sm font-semibold text-fg">
        <Terminal :size="16" /> {{ t('settings.claudeCli') }}
      </h2>
      <p class="text-xs text-fg-subtle">{{ t('settings.claudeCliHint') }}</p>

      <!-- Diagnostic de détection -->
      <div v-if="settings.server" class="card-inset flex flex-wrap items-center gap-x-2 gap-y-1 text-xs">
        <component :is="claudeBin.ok ? CheckCircle2 : AlertCircle" :size="15" :class="claudeBin.ok ? 'text-accent' : 'text-danger'" />
        <span :class="claudeBin.ok ? 'text-fg-muted' : 'text-danger'">
          {{ claudeBin.ok ? t('settings.claudeBinStatusOk') : (claudeBin.source === 'configured' ? t('settings.claudeBinStatusBadPath') : t('settings.claudeBinStatusNotFound')) }}
        </span>
        <code v-if="claudeBin.path" class="min-w-0 truncate font-mono text-fg" :title="claudeBin.path">{{ claudeBin.path }}</code>
        <span v-if="claudeBin.source" class="badge bg-surface-3/40 text-fg-muted">
          {{ claudeBin.source === 'configured' ? t('settings.claudeBinSourceConfigured') : t('settings.claudeBinSourcePath') }}
        </span>
      </div>

      <label class="flex flex-col gap-1">
        <span class="text-xs text-fg-muted">{{ t('settings.claudeBinPathLabel') }}</span>
        <input v-model.trim="claudeBinDraft" class="input font-mono" :placeholder="t('settings.claudeBinPathPlaceholder')" />
        <span class="text-xs text-fg-subtle">{{ t('settings.claudeBinPathHint') }}</span>
      </label>

      <label class="flex flex-col gap-1">
        <span class="text-xs text-fg-muted">{{ t('settings.claudeHomeLabel') }}</span>
        <input v-model.trim="claudeHomeDraft" class="input font-mono" :placeholder="t('settings.claudeHomePlaceholder')" />
        <span class="text-xs text-fg-subtle">{{ t('settings.claudeHomeHint') }}</span>
      </label>

      <div>
        <BaseButton variant="primary" :loading="settings.saving" :disabled="!claudeDirty" @click="saveClaude">
          {{ t('settings.save') }}
        </BaseButton>
      </div>
    </section>

    <!-- Sessions (rétention / archivage auto) -->
    <section class="card flex flex-col gap-3">
      <h2 class="flex items-center gap-2 text-sm font-semibold text-fg">
        <Archive :size="16" /> {{ t('settings.sessions') }}
      </h2>
      <p class="text-xs text-fg-subtle">{{ t('settings.sessionsHint') }}</p>

      <label class="flex flex-col gap-1">
        <span class="text-xs text-fg-muted">{{ t('settings.retentionDays') }}</span>
        <input v-model.number="retentionDraft" type="number" min="0" max="3650" class="input w-32" />
        <span class="text-xs text-fg-subtle">{{ t('settings.retentionDaysHint') }}</span>
      </label>

      <div>
        <BaseButton variant="primary" :loading="settings.saving" :disabled="!retentionDirty" @click="saveRetention">
          {{ t('settings.save') }}
        </BaseButton>
      </div>
    </section>

    <!-- Connexions aux services git (P12) -->
    <GitConnectionsSection />

    <!-- Sécurité & conformité -->
    <section class="card flex flex-col gap-3">
      <h2 class="flex items-center gap-2 text-sm font-semibold text-fg">
        <Shield :size="16" /> {{ t('settings.compliance') }}
      </h2>
      <p class="text-xs text-fg-subtle">{{ t('settings.complianceHint') }}</p>
      <div class="flex flex-wrap gap-2">
        <BaseButton size="sm" :icon="Download" :loading="exporting" @click="exportData">{{ t('settings.exportData') }}</BaseButton>
        <BaseButton variant="ghost" size="sm" :icon="RefreshCw" :loading="auditing" @click="loadAudit">{{ t('settings.refreshAudit') }}</BaseButton>
        <BaseButton variant="ghost" size="sm" :icon="Trash2" @click="deleteMyData">{{ t('settings.deleteData') }}</BaseButton>
      </div>
      <ul v-if="audit.length" class="flex flex-col divide-y divide-border/80 text-xs">
        <li v-for="e in audit" :key="e.id" class="flex items-center justify-between gap-3 py-1.5">
          <span class="font-mono text-fg-muted">{{ e.action }}</span>
          <span class="truncate text-fg-subtle">{{ e.actor }}</span>
          <span class="shrink-0 text-fg-subtle">{{ new Date(e.ts).toLocaleString() }}</span>
        </li>
      </ul>
    </section>

    <!-- Serveur (lecture seule) -->
    <section class="card flex flex-col gap-3">
      <h2 class="flex items-center gap-2 text-sm font-semibold text-fg">
        <Server :size="16" /> {{ t('settings.server') }}
      </h2>
      <p class="text-xs text-fg-subtle">{{ t('settings.serverHint') }}</p>
      <dl v-if="settings.server" class="flex flex-col divide-y divide-border/80">
        <ServerRow :label="t('settings.version')" :value="settings.server.version" />
        <ServerRow :label="t('settings.port')" :value="String(settings.server.port)" flag="--port" />
        <ServerRow :label="t('settings.bind')" :value="settings.server.bind" flag="--bind" />
        <ServerRow
          :label="t('settings.allowedOrigins')"
          :value="settings.server.allowedOrigins.length ? settings.server.allowedOrigins.join(', ') : t('settings.none')"
          flag="--allow-origin"
        />
        <ServerRow :label="t('settings.dataDir')" :value="settings.server.dataDir" />
        <ServerRow :label="t('settings.vapidContact')" :value="settings.server.vapidContact" flag="--vapid-contact" />
        <ServerRow :label="t('settings.vapidPublicKey')" :value="settings.server.vapidPublicKey ?? t('settings.none')" mono />
      </dl>
      <SkeletonRow v-else />
    </section>

    <!-- Support -->
    <section class="card flex flex-col gap-3">
      <h2 class="flex items-center gap-2 text-sm font-semibold text-fg">
        <Coffee :size="16" /> {{ t('settings.support') }}
      </h2>
      <p class="text-xs text-fg-subtle">{{ t('settings.supportHint') }}</p>
      <div>
        <a :href="BUYMEACOFFEE_URL" target="_blank" rel="noopener noreferrer" class="btn-coffee">
          <Coffee :size="16" /> {{ t('settings.supportCta') }}
        </a>
      </div>
    </section>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { AlertCircle, Archive, Bell, BellOff, Check, CheckCircle2, Coffee, Copy, Download, FolderOpen, KeyRound, Monitor, Moon, Plus, RefreshCw, ScanSearch, Send, Server, Shield, SlidersHorizontal, Sun, Terminal, Trash2, X } from '@lucide/vue';
import type {
  AuditLogEntry,
  AuditLogsResponse,
  CreateTokenResponse,
  DataExportResponse,
  DeleteMyDataResponse,
  TokenInfo,
  TokensListResponse,
} from '@arboretum/shared';
import { api, ApiError } from '../lib/api';
import { BUYMEACOFFEE_URL } from '../lib/constants';
import { useSettingsStore } from '../stores/settings';
import { usePushStore } from '../stores/push';
import { useToastsStore } from '../stores/toasts';
import PageHeader from '../components/layout/PageHeader.vue';
import LanguageSwitcher from '../components/LanguageSwitcher.vue';
import BaseButton from '../components/ui/BaseButton.vue';
import SegmentedControl from '../components/ui/SegmentedControl.vue';
import { themeMode, setThemeMode, type ThemeMode } from '../lib/theme';
import SkeletonRow from '../components/ui/SkeletonRow.vue';
import ServerRow from '../components/settings/ServerRow.vue';
import GitConnectionsSection from '../components/settings/GitConnectionsSection.vue';
import DirectoryPicker from '../components/DirectoryPicker.vue';

const { t, locale } = useI18n();
const settings = useSettingsStore();
const push = usePushStore();
const toasts = useToastsStore();

// ---- Préférences ----
const themeOptions = computed(() => [
  { value: 'dark', label: t('theme.dark'), icon: Moon },
  { value: 'light', label: t('theme.light'), icon: Sun },
  { value: 'system', label: t('theme.system'), icon: Monitor },
]);
function onTheme(v: string): void {
  setThemeMode(v as ThemeMode);
}

const testing = ref(false);
async function sendTest(): Promise<void> {
  testing.value = true;
  try {
    await api.post('/api/v1/push/test');
    toasts.success(t('settings.testSent'));
  } catch (e) {
    toasts.error(e);
  } finally {
    testing.value = false;
  }
}

// ---- Tokens ----
const tokens = ref<TokenInfo[]>([]);
const newLabel = ref('');
const creating = ref(false);
const createdToken = ref<CreateTokenResponse | null>(null);
const confirmId = ref<string | null>(null);
const copiedNew = ref(false);

const fmt = (iso: string): string => new Date(iso).toLocaleString(locale.value);

async function loadTokens(): Promise<void> {
  tokens.value = (await api.get<TokensListResponse>('/api/v1/auth/tokens')).tokens;
}

async function createToken(): Promise<void> {
  const label = newLabel.value.trim();
  if (!label) return;
  creating.value = true;
  try {
    createdToken.value = await api.post<CreateTokenResponse>('/api/v1/auth/tokens', { label });
    newLabel.value = '';
    toasts.success(t('settings.tokenCreated'));
    await loadTokens();
  } catch (e) {
    toasts.error(e);
  } finally {
    creating.value = false;
  }
}

async function revoke(tok: TokenInfo): Promise<void> {
  if (confirmId.value !== tok.id) {
    confirmId.value = tok.id;
    return;
  }
  confirmId.value = null;
  try {
    await api.delete(`/api/v1/auth/tokens/${tok.id}`);
    toasts.success(t('settings.tokenRevoked'));
    await loadTokens();
  } catch (e) {
    toasts.error(e instanceof ApiError && e.code === 'LAST_TOKEN' ? t('settings.lastTokenError') : e);
  }
}

async function copy(text: string, target?: 'new'): Promise<void> {
  try {
    await navigator.clipboard.writeText(text);
    if (target === 'new') {
      copiedNew.value = true;
      setTimeout(() => (copiedNew.value = false), 1500);
    }
  } catch {
    /* presse-papiers indisponible (http non sécurisé) : silencieux */
  }
}

// ---- Découverte des dépôts ----
const rootsDraft = ref<string[]>([]);
const intervalDraft = ref(0);
const showRootPicker = ref(false);
const discoveryDirty = computed(
  () =>
    intervalDraft.value !== settings.scanIntervalMin ||
    rootsDraft.value.length !== settings.scanRoots.length ||
    rootsDraft.value.some((r, i) => r !== settings.scanRoots[i]),
);
function syncDiscoveryDraft(): void {
  rootsDraft.value = [...settings.scanRoots];
  intervalDraft.value = settings.scanIntervalMin;
}
function onAddRoot(path: string): void {
  if (!rootsDraft.value.includes(path)) rootsDraft.value.push(path);
  showRootPicker.value = false;
}
async function saveDiscovery(): Promise<void> {
  try {
    await settings.save({ scanRoots: rootsDraft.value, scanIntervalMin: intervalDraft.value });
    syncDiscoveryDraft();
    toasts.success(t('settings.saved'));
  } catch (e) {
    toasts.error(e);
  }
}

// ---- Claude CLI ----
const claudeBinDraft = ref('');
const claudeHomeDraft = ref('');
const claudeBin = computed(() => settings.server?.claudeBin ?? { path: null, source: null, ok: false });
const claudeDirty = computed(
  () => claudeBinDraft.value !== (settings.claudeBinPath ?? '') || claudeHomeDraft.value !== (settings.claudeHome ?? ''),
);
function syncClaudeDraft(): void {
  claudeBinDraft.value = settings.claudeBinPath ?? '';
  claudeHomeDraft.value = settings.claudeHome ?? '';
}
async function saveClaude(): Promise<void> {
  try {
    await settings.save({ claudeBinPath: claudeBinDraft.value, claudeHome: claudeHomeDraft.value });
    syncClaudeDraft();
    toasts.success(t('settings.saved'));
  } catch (e) {
    toasts.error(e);
  }
}

// ---- Sessions (rétention / archivage auto) ----
const retentionDraft = ref(0);
const retentionDirty = computed(() => retentionDraft.value !== settings.retentionDays);
function syncRetentionDraft(): void {
  retentionDraft.value = settings.retentionDays;
}
async function saveRetention(): Promise<void> {
  try {
    await settings.save({ retentionDays: retentionDraft.value });
    syncRetentionDraft();
    toasts.success(t('settings.saved'));
  } catch (e) {
    toasts.error(e);
  }
}

// ---- Sécurité & conformité (audit / RGPD) ----
const audit = ref<AuditLogEntry[]>([]);
const auditing = ref(false);
const exporting = ref(false);

async function loadAudit(): Promise<void> {
  auditing.value = true;
  try {
    audit.value = (await api.get<AuditLogsResponse>('/api/v1/audit-logs?limit=20')).entries;
  } catch (e) {
    toasts.error(e);
  } finally {
    auditing.value = false;
  }
}

async function exportData(): Promise<void> {
  exporting.value = true;
  try {
    const data = await api.get<DataExportResponse>('/api/v1/data/export');
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'arboretum-data-export.json';
    a.click();
    URL.revokeObjectURL(url);
  } catch (e) {
    toasts.error(e);
  } finally {
    exporting.value = false;
  }
}

async function deleteMyData(): Promise<void> {
  if (!window.confirm(t('settings.deleteConfirm'))) return;
  try {
    const pending = await api.post<DeleteMyDataResponse>('/api/v1/data/delete-my-data', {});
    if (pending.status !== 'pending' || !pending.confirm) return;
    const done = await api.post<DeleteMyDataResponse>('/api/v1/data/delete-my-data', { confirm: pending.confirm });
    toasts.success(t('settings.deleteDone'));
    if (done.summary.tokenRevoked) window.location.reload(); // token courant révoqué → re-login
  } catch (e) {
    toasts.error(e);
  }
}

// P11 : un settings_update WS (autre onglet/client) re-synchronise un draft SCALAIRE uniquement
// s'il n'a pas été modifié localement (le draft valait encore l'ancienne valeur du store) → on
// n'écrase jamais une saisie en cours.
watch(() => settings.scanIntervalMin, (n, o) => { if (intervalDraft.value === o) intervalDraft.value = n; });
watch(() => settings.retentionDays, (n, o) => { if (retentionDraft.value === o) retentionDraft.value = n; });
watch(() => settings.claudeBinPath, (n, o) => { if (claudeBinDraft.value === (o ?? '')) claudeBinDraft.value = n ?? ''; });
watch(() => settings.claudeHome, (n, o) => { if (claudeHomeDraft.value === (o ?? '')) claudeHomeDraft.value = n ?? ''; });

onMounted(async () => {
  await Promise.all([loadTokens(), settings.loaded ? Promise.resolve() : settings.fetch()]);
  syncDiscoveryDraft();
  syncClaudeDraft();
  syncRetentionDraft();
  void push.refresh();
});
// Le temps réel des réglages est géré globalement par useRealtimeBootstrap (App.vue).
</script>
