import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, map, tap } from 'rxjs';
import { Router } from '@angular/router';
import { ReponseAuth, ReponseRefresh, Utilisateur } from './models';

/**
 * Service central d'authentification.
 *
 * Stratégie de stockage (cf. contrat d'API) :
 * - access_token : conservé EN MÉMOIRE uniquement (variable privée), jamais persisté
 *   pour limiter l'exposition en cas de XSS.
 * - refresh_token : persisté dans localStorage afin de rétablir la session au
 *   rechargement de la page.
 */
@Injectable({ providedIn: 'root' })
export class AuthService {
  private readonly http = inject(HttpClient);
  private readonly router = inject(Router);

  private static readonly CLE_REFRESH = 'pdfeditor_refresh_token';

  /** access_token en mémoire (perdu au rechargement, restauré via refresh) */
  private accessToken: string | null = null;

  private readonly utilisateurSubject =
    new BehaviorSubject<Utilisateur | null>(null);
  readonly utilisateur$ = this.utilisateurSubject.asObservable();
  readonly estConnecte$ = this.utilisateur$.pipe(map((u) => u !== null));

  /** Indique si l'on possède un refresh_token persistant (session potentielle). */
  aUneSessionPotentielle(): boolean {
    return this.getRefreshToken() !== null;
  }

  getAccessToken(): string | null {
    return this.accessToken;
  }

  getRefreshToken(): string | null {
    return localStorage.getItem(AuthService.CLE_REFRESH);
  }

  // --- Endpoints d'authentification --------------------------------------

  register(email: string, password: string): Observable<ReponseAuth> {
    return this.http
      .post<ReponseAuth>('/api/auth/register', { email, password })
      .pipe(tap((res) => this.appliquerSession(res)));
  }

  login(email: string, password: string): Observable<ReponseAuth> {
    return this.http
      .post<ReponseAuth>('/api/auth/login', { email, password })
      .pipe(tap((res) => this.appliquerSession(res)));
  }

  /**
   * Rafraîchit le couple de tokens à partir du refresh_token persistant.
   * Utilisé par l'intercepteur sur 401 et au démarrage de l'app.
   */
  refresh(): Observable<ReponseRefresh> {
    const refreshToken = this.getRefreshToken();
    return this.http
      .post<ReponseRefresh>('/api/auth/refresh', {
        refresh_token: refreshToken,
      })
      .pipe(
        tap((res) => {
          this.accessToken = res.access_token;
          this.enregistrerRefreshToken(res.refresh_token);
        }),
      );
  }

  /** Récupère le profil courant (utilisé après un refresh au démarrage). */
  chargerProfil(): Observable<Utilisateur> {
    return this.http
      .get<Utilisateur>('/api/me')
      .pipe(tap((u) => this.utilisateurSubject.next(u)));
  }

  logout(): Observable<void> {
    // On tente d'invalider le refresh côté serveur ; quoi qu'il arrive on purge en local.
    const finaliser = () => {
      this.viderSession();
      this.router.navigate(['/login']);
    };
    return new Observable<void>((observer) => {
      this.http.post<void>('/api/auth/logout', {}).subscribe({
        next: () => {
          finaliser();
          observer.next();
          observer.complete();
        },
        error: () => {
          finaliser();
          observer.next();
          observer.complete();
        },
      });
    });
  }

  // --- Helpers internes ---------------------------------------------------

  private appliquerSession(res: ReponseAuth): void {
    this.accessToken = res.access_token;
    this.enregistrerRefreshToken(res.refresh_token);
    this.utilisateurSubject.next(res.user);
  }

  private enregistrerRefreshToken(token: string): void {
    localStorage.setItem(AuthService.CLE_REFRESH, token);
  }

  /** Efface toute trace de session (utilisé au logout et sur refresh échoué). */
  viderSession(): void {
    this.accessToken = null;
    localStorage.removeItem(AuthService.CLE_REFRESH);
    this.utilisateurSubject.next(null);
  }
}
