import { Component, OnDestroy, OnInit } from '@angular/core';
import { interval, switchMap, Subscription } from 'rxjs';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { LucideAngularModule, ArrowLeft, RefreshCw, Save, Check, AlertCircle, Download, Trash2, Plus, X, Heart, Link2, Unlink } from 'lucide-angular';
import { SettingsService, AppSettings, AppSettingsUpdate, OneMinModelGroup, OpenRouterModel, OllamaPullEvent } from '../services/settings.service';
import { UpdatesService, UpdateStatus } from '../services/updates.service';
import { ConfigService } from '../services/config.service';
import { LayoutService } from '../services/layout.service';
import { LicenseService, LicenseStatusDTO, BetaStatusDTO, ChannelStatusDTO, ChannelName } from '../services/license.service';
import { ConfirmDialogService } from '../shared/confirm-dialog/confirm-dialog.service';

/**
 * Ecran de parametrage du LLM utilise par le Brain.
 *
 * Deux providers au choix :
 *  - Ollama (local) : on liste dynamiquement les modeles installes.
 *  - 1min.ai (cloud) : on fournit une cle API + on choisit dans un catalogue fixe.
 *
 * Les modifications sont persistees cote Brain dans data/settings.json
 * (fichier local, usage mono-utilisateur) et appliquees a la prochaine
 * requete chat / generate — pas besoin de redemarrer.
 */
@Component({
  selector: 'app-settings',
  standalone: true,
  imports: [CommonModule, FormsModule, LucideAngularModule],
  templateUrl: './settings.component.html',
  styleUrls: ['./settings.component.scss']
})
export class SettingsComponent implements OnInit, OnDestroy {

  readonly ArrowLeft = ArrowLeft;
  readonly RefreshCw = RefreshCw;
  readonly Save = Save;
  readonly Check = Check;
  readonly AlertCircle = AlertCircle;
  readonly Download = Download;
  readonly Trash2 = Trash2;
  readonly Plus = Plus;
  readonly X = X;
  readonly Heart = Heart;
  readonly Link2 = Link2;
  readonly Unlink = Unlink;

  // --- Licence Patreon (canal beta) ---
  licenseStatus: LicenseStatusDTO | null = null;
  licenseLoading = false;
  licenseError = '';
  /** Token JWT colle par l'utilisateur apres OAuth. */
  licenseJwtInput = '';
  /** Etat du canal beta (digests des images privees). */
  betaStatus: BetaStatusDTO | null = null;
  betaChecking = false;

  // --- Bascule de canal stable <-> beta via sidecar switcher ---
  channelStatus: ChannelStatusDTO | null = null;
  /** True pendant le polling apres clic. Bloque les boutons. */
  switchInFlight = false;
  /** ID de la commande de switch en cours, pour ignorer les vieux resultats. */
  private switchCommandId: string | null = null;
  /** Subscription du polling pour pouvoir l'arreter. */
  private switchPollSub: Subscription | null = null;
  /** Erreur affichee si le switch a echoue. */
  switchError = '';

  // --- Pull / delete de modeles Ollama ---
  /** Dialog d'ajout de modele ouvert/ferme. */
  pullDialogOpen = false;
  /** Nom saisi par l'utilisateur dans le dialog. */
  pullModelName = '';
  /** Suggestions courantes affichees dans le dialog. */
  readonly pullSuggestions = [
    'gemma4:e4b', 'gemma3:4b', 'gemma3:12b',
    'llama3.2:3b', 'llama3.1:8b',
    'mistral:7b', 'qwen2.5:3b', 'qwen2.5:7b'
  ];
  /** Pull en cours ; null si aucun. */
  pullInProgress = false;
  /** Etape courante affichee a l'utilisateur (ex: "downloading", "verifying"). */
  pullStatus = '';
  /** Bytes telecharges sur le digest courant. */
  pullCompleted = 0;
  /** Bytes totaux du digest courant. */
  pullTotal = 0;
  /** Souscription au flux de pull pour pouvoir l'annuler. */
  private pullSubscription: Subscription | null = null;
  /** True si on a recu un evenement {status:"success"} d'Ollama. Sans ca,
   *  une fermeture de stream (timeout proxy, perte reseau) ne doit PAS etre
   *  interpretee comme une reussite. */
  private pullSucceeded = false;

  /** Modele en cours de suppression (nom) pour disabler son bouton. */
  deletingModel: string | null = null;

  // Mises a jour conteneurs
  updateStatus: UpdateStatus | null = null;
  updateChecking = false;
  updateApplying = false;
  updateMessage = '';

  settings: AppSettings | null = null;
  ollamaModels: string[] = [];
  oneminGroups: OneMinModelGroup[] = [];

  /** Catalogue OpenRouter (chargé dynamiquement) + filtre "gratuits seulement". */
  openrouterModels: OpenRouterModel[] = [];
  openrouterFreeOnly = true;
  /** Fournisseur 1min.ai actuellement selectionne (filtre la liste des modeles). */
  oneminProvider: string = '';

  loadingModels = false;
  saving = false;
  errorMessage = '';
  successMessage = '';

  /**
   * Fenetre de contexte max supportee par le modele Ollama actuellement
   * selectionne (extraite des metadonnees GGUF via /api/show). 0 si inconnue
   * — dans ce cas on laisse un fallback de 131072 cote UI.
   */
  ollamaModelMaxContext = 0;

  /** Minimum raisonnable pour num_ctx (defaut Ollama = 2048). */
  readonly CTX_MIN = 2048;
  /** Fallback si Ollama ne renvoie pas le context_length (modele exotique). */
  readonly CTX_FALLBACK_MAX = 131072;

  /** Cle 1min.ai saisie — vide = on ne touche pas a la cle persistee. */
  oneminApiKeyInput = '';
  /** True si l'utilisateur a coche "effacer la cle". */
  clearApiKey = false;

  /** Cle OpenRouter saisie — vide = on ne touche pas a la cle persistee. */
  openrouterApiKeyInput = '';
  /** True si l'utilisateur a coche "effacer la cle OpenRouter". */
  clearOpenrouterKey = false;

  constructor(
    private settingsService: SettingsService,
    private router: Router,
    private updatesService: UpdatesService,
    public config: ConfigService,
    private licenseService: LicenseService,
    private confirmDialog: ConfirmDialogService,
    private layoutService: LayoutService
  ) {}

  ngOnInit(): void {
    // Page racine : on s'assure de ne pas heriter de la sidebar d'une
    // section precedente (cf. fix CampaignsComponent / LoreComponent).
    this.layoutService.hide();
    this.loadSettings();
    if (this.config.updateCheckEnabled) {
      this.checkUpdates();
    }
    this.loadLicense();
    this.loadChannelStatus();
  }

  // --- Licence Patreon ---------------------------------------------------

  loadLicense(): void {
    this.licenseLoading = true;
    this.licenseService.getStatus().subscribe({
      next: (s) => {
        this.licenseStatus = s;
        this.licenseLoading = false;
        if (s?.enabled && (s.status === 'VALID' || s.status === 'GRACE') && s.betaChannelEnabled) {
          this.checkBeta();
        }
      },
      error: () => { this.licenseLoading = false; }
    });
  }

  /**
   * Ouvre la page OAuth Patreon dans une nouvelle fenetre.
   * L'utilisateur copie ensuite le JWT et le colle dans l'input ci-dessous.
   */
  connectPatreon(): void {
    this.licenseError = '';
    this.licenseService.getConnectUrl().subscribe({
      next: (r) => {
        if (!r?.url) {
          this.licenseError = 'Impossible de generer l\'URL de connexion. Verifie ta config.';
          return;
        }
        window.open(r.url, '_blank', 'noopener');
      }
    });
  }

  installLicense(): void {
    const jwt = this.licenseJwtInput.trim();
    if (!jwt) {
      this.licenseError = 'Colle d\'abord le token recu apres connexion Patreon.';
      return;
    }
    this.licenseError = '';
    this.licenseService.install(jwt).subscribe((res) => {
      if ((res as any)?.error) {
        this.licenseError = (res as any).error;
        return;
      }
      this.licenseStatus = res as LicenseStatusDTO;
      this.licenseJwtInput = '';
      this.successMessage = 'Compte Patreon connecte. L\'acces beta est actif.';
      if (this.licenseStatus.betaChannelEnabled) {
        this.checkBeta();
      }
    });
  }

  refreshLicense(): void {
    this.licenseLoading = true;
    this.licenseService.refresh().subscribe({
      next: (s) => {
        this.licenseStatus = s;
        this.licenseLoading = false;
      },
      error: () => { this.licenseLoading = false; }
    });
  }

  disconnectPatreon(): void {
    this.confirmDialog.confirm({
      title: 'Deconnecter Patreon',
      message: 'Deconnecter ton compte Patreon ?',
      details: ['Tu perdras l\'acces au canal beta.'],
      confirmLabel: 'Deconnecter',
      variant: 'warning'
    }).then(ok => {
      if (!ok) return;
      this.licenseService.disconnect().subscribe(() => {
        this.licenseStatus = null;
        this.betaStatus = null;
        this.successMessage = 'Compte Patreon deconnecte.';
        this.loadLicense();
      });
    });
  }

  toggleBetaChannel(enabled: boolean): void {
    this.licenseService.setBetaChannel(enabled).subscribe({
      next: (s) => {
        if (s) this.licenseStatus = s;
        if (enabled) this.checkBeta();
        else this.betaStatus = null;
      }
    });
  }

  checkBeta(): void {
    this.betaChecking = true;
    this.licenseService.checkBeta().subscribe({
      next: (s) => {
        this.betaStatus = s;
        this.betaChecking = false;
      },
      error: () => { this.betaChecking = false; }
    });
  }

  // --- Bascule de canal stable <-> beta --------------------------------------

  loadChannelStatus(): void {
    this.licenseService.getChannelStatus().subscribe({
      next: (s) => {
        this.channelStatus = s;
        // Si on revient sur l'ecran apres un reload (post-switch reussi),
        // on affiche le dernier resultat eventuel jusqu'a interaction utilisateur.
      },
      error: () => { this.channelStatus = null; }
    });
  }

  /**
   * Declenche un switch de canal. La sequence cote UI :
   *   1. Confirm modal (action destructrice : recreate des containers)
   *   2. POST /api/license/channel/switch -> 202 avec l'ID de la commande
   *   3. Polling /api/license/channel toutes les 2s jusqu'a status != IN_PROGRESS
   *   4. Si SUCCESS : la page va se rendre injoignable (Core recree). On affiche
   *      "Recharge la page dans quelques secondes" et on essaie de poll quand
   *      meme — au retour de Core, on detectera SUCCESS et on rechargera auto.
   *   5. Si ERROR : on affiche le message d'erreur et on debloque les boutons.
   */
  requestChannelSwitch(target: ChannelName): void {
    const confirmMessage = target === 'beta'
      ? 'Basculer LoreMind sur le canal beta ? Les containers core/brain/web vont etre recrees avec les images beta. L\'application sera indisponible 10-30 secondes.'
      : 'Repasser LoreMind sur le canal stable ? Les containers core/brain/web vont etre recrees avec les images stables. L\'application sera indisponible 10-30 secondes.';

    this.confirmDialog.confirm({
      title: target === 'beta' ? 'Passer en beta ?' : 'Repasser en stable ?',
      message: confirmMessage,
      details: [
        'Les donnees (DB, images) sont preservees.',
        'Tu pourras refaire le chemin inverse a tout moment depuis cet ecran.'
      ],
      confirmLabel: target === 'beta' ? 'Passer en beta' : 'Repasser en stable',
      variant: 'warning'
    }).then(ok => {
      if (!ok) return;
      this.doChannelSwitch(target);
    });
  }

  private doChannelSwitch(target: ChannelName): void {
    this.switchInFlight = true;
    this.switchError = '';
    this.licenseService.switchChannel(target).subscribe((res) => {
      if ('error' in res) {
        this.switchError = res.error;
        this.switchInFlight = false;
        return;
      }
      this.switchCommandId = res.id;
      this.startSwitchPolling();
    });
  }

  /**
   * Poll /api/license/channel toutes les 2s. S'arrete quand on detecte un
   * resultat avec un ID >= a celui qu'on a soumis (le sidecar le met a jour
   * a la fin de son traitement).
   */
  private startSwitchPolling(): void {
    this.stopSwitchPolling();
    this.switchPollSub = interval(2000).pipe(
      switchMap(() => this.licenseService.getChannelStatus())
    ).subscribe((status) => {
      if (!status) return;
      this.channelStatus = status;
      const last = status.lastSwitch;
      if (!last || last.id !== this.switchCommandId) return;
      if (last.status === 'SUCCESS') {
        // La page va se rafraichir auto via l'update-banner qui detecte le
        // restart de Core. On laisse switchInFlight a true pour bloquer
        // toute autre action en attendant.
        this.stopSwitchPolling();
        this.switchInFlight = false;
      } else if (last.status === 'ERROR') {
        this.switchError = last.message || 'Echec du switch';
        this.stopSwitchPolling();
        this.switchInFlight = false;
      }
      // IN_PROGRESS : on continue a poll.
    });
  }

  private stopSwitchPolling(): void {
    if (this.switchPollSub) {
      this.switchPollSub.unsubscribe();
      this.switchPollSub = null;
    }
  }

  ngOnDestroy(): void {
    this.stopSwitchPolling();
    if (this.pullSubscription) {
      this.pullSubscription.unsubscribe();
    }
  }

  /**
   * Mapping tier_id Patreon → nom lisible. Les IDs viennent du dashboard
   * Patreon de LoreMind (Settings -> Tiers). Sans entree dans la map, on
   * affiche l'ID brut pour rester debuggable.
   *
   * Si tu ajoutes un nouveau tier Patreon, complete cette map et redeploie.
   * (Pas besoin de toucher au backend — c'est juste un libelle d'UI.)
   */
  private static readonly TIER_LABELS: Record<string, string> = {
    '28448887': 'Compagnon',
    // '0000000': 'Aventurier',
    // '0000000': 'Heros',
  };

  /** Libelle lisible d'un tier Patreon, fallback sur l'ID brut. */
  tierLabel(tierId: string | null | undefined): string {
    if (!tierId) return '';
    return SettingsComponent.TIER_LABELS[tierId] ?? tierId;
  }

  /** Format human-readable des dates renvoyees par le backend. */
  formatDate(iso: string | null | undefined): string {
    if (!iso) return '';
    try { return new Date(iso).toLocaleString(); } catch { return iso; }
  }

  /** Nombre de jours restants avant expiration JWT (peut etre negatif). */
  get daysUntilExpiry(): number | null {
    if (!this.licenseStatus?.expiresAt) return null;
    const exp = new Date(this.licenseStatus.expiresAt).getTime();
    const now = Date.now();
    return Math.ceil((exp - now) / (1000 * 60 * 60 * 24));
  }

  checkUpdates(): void {
    this.updateChecking = true;
    this.updateMessage = '';
    this.updatesService.checkNow().subscribe({
      next: (s) => {
        this.updateStatus = s;
        this.updateChecking = false;
      },
      error: () => {
        this.updateChecking = false;
      }
    });
  }

  applyUpdate(): void {
    this.confirmDialog.confirm({
      title: 'Mettre a jour',
      message: 'Telecharger et redemarrer les conteneurs maintenant ?',
      details: ['L\'app sera indisponible quelques secondes.'],
      confirmLabel: 'Mettre à jour',
      variant: 'warning'
    }).then(ok => {
      if (!ok) return;
      this.updateApplying = true;
      this.updateMessage = '';
      this.updatesService.apply().subscribe({
        next: (r) => {
          this.updateApplying = false;
          // Le redemarrage de core peut couper la connexion avant la reponse —
          // dans ce cas r vaut null (gere par catchError dans le service).
          this.updateMessage = r?.message
            ?? 'Mise a jour declenchee. Rechargez la page dans 30s.';
        },
        error: () => {
          this.updateApplying = false;
          this.updateMessage = 'Mise a jour declenchee. Rechargez la page dans 30s.';
        }
      });
    });
  }

  loadSettings(): void {
    this.settingsService.getSettings().subscribe({
      next: (s) => {
        this.settings = { ...s };
        this.refreshModels();
        this.fetchOllamaModelInfo();
      },
      error: (err) => this.errorMessage = this.extractError(err, 'Impossible de charger les parametres.')
    });
  }

  refreshModels(): void {
    if (!this.settings) return;
    this.loadingModels = true;

    this.settingsService.listOllamaModels().subscribe({
      next: (r) => this.ollamaModels = r.models,
      error: () => this.ollamaModels = [],
      complete: () => this.loadingModels = false
    });

    this.settingsService.listOneMinModels().subscribe({
      next: (r) => {
        this.oneminGroups = r.groups;
        this.syncOneminProviderFromModel();
      },
      error: () => this.oneminGroups = []
    });

    this.settingsService.listOpenRouterModels().subscribe({
      next: (r) => this.openrouterModels = r.models,
      error: () => this.openrouterModels = []
    });
  }

  /** Modeles OpenRouter, filtres (gratuits seulement par defaut). */
  get filteredOpenrouterModels(): OpenRouterModel[] {
    return this.openrouterFreeOnly
      ? this.openrouterModels.filter(m => m.free)
      : this.openrouterModels;
  }

  /** Options du select OpenRouter : routeur auto + liste filtree + valeur courante. */
  get openrouterSelectOptions(): { id: string; label: string }[] {
    const options: { id: string; label: string }[] = [
      { id: 'openrouter/free', label: 'openrouter/free — routeur auto (gratuit)' }
    ];
    for (const m of this.filteredOpenrouterModels) {
      const ctx = m.context_length ? ` — ${m.context_length.toLocaleString()} ctx` : '';
      options.push({ id: m.id, label: `${m.id}${ctx}${m.free ? ' · gratuit' : ''}` });
    }
    // Garantit que le modele actuellement configure reste selectionnable.
    const cur = this.settings?.openrouter_model;
    if (cur && !options.some(o => o.id === cur)) {
      options.splice(1, 0, { id: cur, label: `${cur} (actuel)` });
    }
    return options;
  }

  /** Deduit le fournisseur a partir du modele actuellement configure. */
  private syncOneminProviderFromModel(): void {
    if (!this.settings) return;
    const currentModel = this.settings.onemin_model;
    const found = this.oneminGroups.find(g => g.models.includes(currentModel));
    this.oneminProvider = found ? found.provider : (this.oneminGroups[0]?.provider ?? '');
  }

  /** Retourne la liste des modeles du fournisseur selectionne. */
  get currentProviderModels(): string[] {
    const group = this.oneminGroups.find(g => g.provider === this.oneminProvider);
    return group ? group.models : [];
  }

  /**
   * Recupere la fenetre max supportee par le modele Ollama selectionne.
   * Si la valeur courante de num_ctx depasse ce max, on la clamp.
   */
  fetchOllamaModelInfo(): void {
    if (!this.settings || this.settings.llm_provider !== 'ollama') return;
    const modelName = this.settings.llm_model;
    if (!modelName) return;
    this.settingsService.getOllamaModelInfo(modelName).subscribe({
      next: (info) => {
        this.ollamaModelMaxContext = info.context_length;
        const max = this.effectiveMaxContext;
        if (this.settings && this.settings.llm_num_ctx > max) {
          this.settings.llm_num_ctx = max;
        }
      },
      error: () => this.ollamaModelMaxContext = 0
    });
  }

  /** Max effectif a afficher pour le slider (modele Ollama ou fallback). */
  get effectiveMaxContext(): number {
    return this.ollamaModelMaxContext > 0 ? this.ollamaModelMaxContext : this.CTX_FALLBACK_MAX;
  }

  /** Quand on change de fournisseur, bascule automatiquement sur son premier modele. */
  onProviderChange(): void {
    if (!this.settings) return;
    const models = this.currentProviderModels;
    if (models.length > 0 && !models.includes(this.settings.onemin_model)) {
      this.settings.onemin_model = models[0];
    }
  }

  save(): void {
    if (!this.settings) return;
    this.saving = true;
    this.errorMessage = '';
    this.successMessage = '';

    const patch: AppSettingsUpdate = {
      llm_provider: this.settings.llm_provider,
      ollama_base_url: this.settings.ollama_base_url,
      llm_model: this.settings.llm_model,
      onemin_model: this.settings.onemin_model,
      openrouter_model: this.settings.openrouter_model,
      llm_num_ctx: this.settings.llm_num_ctx,
      import_chunk_tokens: this.settings.import_chunk_tokens,
      llm_timeout_seconds: this.settings.llm_timeout_seconds
    };
    if (this.clearApiKey) {
      patch.onemin_api_key = '';
    } else if (this.oneminApiKeyInput.trim()) {
      patch.onemin_api_key = this.oneminApiKeyInput.trim();
    }
    if (this.clearOpenrouterKey) {
      patch.openrouter_api_key = '';
    } else if (this.openrouterApiKeyInput.trim()) {
      patch.openrouter_api_key = this.openrouterApiKeyInput.trim();
    }

    this.settingsService.updateSettings(patch).subscribe({
      next: (s) => {
        this.settings = { ...s };
        this.oneminApiKeyInput = '';
        this.clearApiKey = false;
        this.openrouterApiKeyInput = '';
        this.clearOpenrouterKey = false;
        this.successMessage = 'Parametres sauvegardes.';
        this.saving = false;
      },
      error: (err) => {
        this.errorMessage = this.extractError(err, 'Echec de la sauvegarde.');
        this.saving = false;
      }
    });
  }

  // --- Gestion des modeles Ollama (pull / delete) -------------------------

  openPullDialog(): void {
    this.pullDialogOpen = true;
    this.pullModelName = '';
    this.resetPullState();
  }

  closePullDialog(): void {
    if (this.pullInProgress) return; // empêche fermeture pendant un pull
    this.pullDialogOpen = false;
  }

  selectSuggestion(name: string): void {
    this.pullModelName = name;
  }

  startPull(): void {
    const name = this.pullModelName.trim();
    if (!name || this.pullInProgress) return;
    this.resetPullState();
    this.pullInProgress = true;
    this.pullStatus = 'connexion...';
    this.errorMessage = '';

    this.pullSubscription = this.settingsService.pullOllamaModel(name).subscribe({
      next: (event: OllamaPullEvent) => {
        if (event.error) {
          this.errorMessage = `Echec : ${event.error}`;
          this.pullInProgress = false;
          return;
        }
        if (event.status) this.pullStatus = event.status;
        if (event.completed != null) this.pullCompleted = event.completed;
        if (event.total != null) this.pullTotal = event.total;
        // Marqueur explicite : Ollama emet "success" en derniere ligne quand
        // le pull est reellement complet (manifest + layers + verify).
        if (event.status === 'success') this.pullSucceeded = true;
      },
      error: (err) => {
        this.errorMessage = this.extractError(err, `Echec du telechargement de ${name}.`);
        this.pullInProgress = false;
      },
      complete: () => {
        this.pullInProgress = false;
        if (!this.pullSucceeded) {
          // Stream ferme sans 'success' final = connexion coupee
          // (timeout proxy, perte reseau, ...). Le modele est probablement
          // partiellement telecharge ; Ollama gardera les couches deja DL.
          this.errorMessage = `Telechargement de ${name} interrompu avant la fin. Relancez pour reprendre.`;
          this.refreshModels();
          return;
        }
        this.successMessage = `Modele ${name} telecharge.`;
        this.refreshModels();
        // Si l'utilisateur n'avait aucun modele, on selectionne celui-ci.
        if (this.settings && !this.settings.llm_model) {
          this.settings.llm_model = name;
          this.fetchOllamaModelInfo();
        }
        // Petite tempo avant de fermer pour que le user voie "success".
        setTimeout(() => this.closePullDialog(), 1200);
      }
    });
  }

  cancelPull(): void {
    if (this.pullSubscription) {
      this.pullSubscription.unsubscribe();
      this.pullSubscription = null;
    }
    this.pullInProgress = false;
    this.pullStatus = 'annule';
  }

  private resetPullState(): void {
    this.pullStatus = '';
    this.pullCompleted = 0;
    this.pullTotal = 0;
    this.pullSucceeded = false;
    if (this.pullSubscription) {
      this.pullSubscription.unsubscribe();
      this.pullSubscription = null;
    }
  }

  /** Pourcentage du digest courant pour la barre de progression. */
  get pullPercent(): number {
    if (this.pullTotal <= 0) return 0;
    return Math.min(100, Math.round((this.pullCompleted / this.pullTotal) * 100));
  }

  /** Affichage humain des octets ('1.2 GB' / '450 MB'). */
  formatBytes(b: number): string {
    if (!b) return '0';
    const u = ['B', 'KB', 'MB', 'GB', 'TB'];
    let i = 0;
    let v = b;
    while (v >= 1024 && i < u.length - 1) { v /= 1024; i++; }
    return `${v.toFixed(v < 10 && i > 0 ? 1 : 0)} ${u[i]}`;
  }

  deleteModel(name: string): void {
    this.confirmDialog.confirm({
      title: 'Supprimer le modele',
      message: `Supprimer le modele '${name}' ?`,
      details: ['L\'espace disque sera libere.'],
      confirmLabel: 'Supprimer',
      variant: 'danger'
    }).then(ok => {
      if (!ok) return;
      this.deletingModel = name;
      this.errorMessage = '';
      this.settingsService.deleteOllamaModel(name).subscribe({
        next: () => {
          this.deletingModel = null;
          this.successMessage = `Modele ${name} supprime.`;
          // Si l'utilisateur supprime le modele actuellement selectionne,
          // on bascule sur le premier disponible (ou vide).
          this.refreshModels();
          if (this.settings && this.settings.llm_model === name) {
            this.settings.llm_model = '';
            this.ollamaModelMaxContext = 0;
          }
        },
        error: (err) => {
          this.deletingModel = null;
          this.errorMessage = this.extractError(err, `Echec de la suppression de ${name}.`);
        }
      });
    });
  }

  goBack(): void {
    this.router.navigate(['/lore']);
  }

  private extractError(err: any, fallback: string): string {
    if (err?.error?.detail) return String(err.error.detail);
    if (err?.message) return err.message;
    return fallback;
  }
}
