// auth.service.ts
import { isPlatformBrowser } from '@angular/common';
import { HttpClient, HttpBackend, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable, inject, PLATFORM_ID } from '@angular/core';
import { FirebaseApp } from '@angular/fire/app';
import { GoogleAuthProvider, UserCredential, createUserWithEmailAndPassword, getAuth, signInWithCustomToken, signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth';
import { BehaviorSubject, Observable, catchError, firstValueFrom, map, of, shareReplay, take, tap } from 'rxjs';
import { environment } from '../../environments/environment';
import { MailService } from './mail.service';

export type UserRole = 'admin' | 'pro' | 'guest';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  private app = inject(FirebaseApp);
  private platformId = inject(PLATFORM_ID);
  private isBrowser = isPlatformBrowser(this.platformId);
  private auth = this.isBrowser ? getAuth(this.app) : null;
  private http = new HttpClient(inject(HttpBackend));
  private mailService = inject(MailService);
  // Add a BehaviorSubject to track the current user role
  private userRoleSubject = new BehaviorSubject<UserRole | null>(null);
  public userRole$ = this.userRoleSubject.asObservable();
  // Referenz für den gecachten API-Aufruf
  private cachedUserRole$: Observable<UserRole | null> | null = null;

  // Zeitraum in ms, nach dem der Cache zurückgesetzt werden soll (z.B. 5 Minuten)
  private cacheDuration = 5 * 60 * 1000;
  private lastCacheTime = 0;
  constructor() {
    // Load role from token when service is initialized
    this.loadRoleFromToken();
  }

  // Helper methods for localStorage access (only in browser)
  private setLocalStorageItem(key: string, value: string): void {
    if (this.isBrowser) {
      localStorage.setItem(key, value);
    }
  }

  private getLocalStorageItem(key: string): string | null {
    if (this.isBrowser) {
      return localStorage.getItem(key);
    }
    return null;
  }

  private removeLocalStorageItem(key: string): void {
    if (this.isBrowser) {
      localStorage.removeItem(key);
    }
  }

  private loadRoleFromToken(): void {
    this.getToken().then(token => {
      if (token) {
        const role = this.extractRoleFromToken(token);
        this.userRoleSubject.next(role);
      } else {
        this.userRoleSubject.next(null);
      }
    });
  }

  private extractRoleFromToken(token: string): UserRole | null {
    try {
      const payloadBase64 = token.split('.')[1];
      const payloadJson = atob(payloadBase64.replace(/-/g, '+').replace(/_/g, '/'));
      const payload = JSON.parse(payloadJson);
      return (payload.role as UserRole) || null;
    } catch (e) {
      return null;
    }
  }
  // Registrierung mit Email und Passwort
  async registerWithEmail(email: string, password: string): Promise<UserCredential> {
    if (!this.isBrowser || !this.auth) {
      throw new Error('Auth is only available in browser context');
    }

    // Bestimmen der aktuellen Umgebung/Domain für die Verifizierungs-URL
    let verificationUrl = 'https://www.bizmatch.net/email-authorized';

    // Prüfen der aktuellen Umgebung basierend auf dem Host (nur im Browser)
    if (this.isBrowser) {
      const currentHost = window.location.hostname;

      if (currentHost.includes('localhost')) {
        verificationUrl = 'http://localhost:4200/email-authorized';
      } else if (currentHost.includes('dev.bizmatch.net')) {
        verificationUrl = 'https://dev.bizmatch.net/email-authorized';
      } else {
        verificationUrl = 'https://www.bizmatch.net/email-authorized';
      }
    }

    // ActionCode-Einstellungen mit der dynamischen URL
    const actionCodeSettings = {
      url: `${verificationUrl}?email=${email}`,
      handleCodeInApp: true,
    };

    // Benutzer erstellen
    const userCredential = await createUserWithEmailAndPassword(this.auth, email, password);

    // E-Mail-Verifizierung mit den angepassten ActionCode-Einstellungen senden
    if (userCredential.user) {
      //await sendEmailVerification(userCredential.user, actionCodeSettings);
      this.mailService.sendVerificationEmail(userCredential.user.email).subscribe({
        next: () => {
          console.log('Verification email sent successfully');
          // Erfolgsmeldung anzeigen
        },
        error: error => {
          console.error('Error sending verification email', error);
          // Fehlermeldung anzeigen
        },
      });
    }

    // const token = await userCredential.user.getIdToken();
    // this.setLocalStorageItem('authToken', token);
    // this.setLocalStorageItem('refreshToken', userCredential.user.refreshToken);
    // if (userCredential.user.photoURL) {
    //   this.setLocalStorageItem('photoURL', userCredential.user.photoURL);
    // }

    return userCredential;
  }

  // Login mit Email und Passwort
  loginWithEmail(email: string, password: string): Promise<UserCredential> {
    if (!this.isBrowser || !this.auth) {
      throw new Error('Auth is only available in browser context');
    }
    return signInWithEmailAndPassword(this.auth, email, password).then(async userCredential => {
      if (userCredential.user) {
        const token = await userCredential.user.getIdToken();
        this.setLocalStorageItem('authToken', token);
        this.setLocalStorageItem('refreshToken', userCredential.user.refreshToken);
        if (userCredential.user.photoURL) {
          this.setLocalStorageItem('photoURL', userCredential.user.photoURL);
        }
        this.loadRoleFromToken();
      }
      return userCredential;
    });
  }

  // Login mit Google
  loginWithGoogle(): Promise<UserCredential> {
    if (!this.isBrowser || !this.auth) {
      throw new Error('Auth is only available in browser context');
    }
    const provider = new GoogleAuthProvider();
    return signInWithPopup(this.auth, provider).then(async userCredential => {
      if (userCredential.user) {
        const token = await userCredential.user.getIdToken();
        this.setLocalStorageItem('authToken', token);
        this.setLocalStorageItem('refreshToken', userCredential.user.refreshToken);
        if (userCredential.user.photoURL) {
          this.setLocalStorageItem('photoURL', userCredential.user.photoURL);
        }
        this.loadRoleFromToken();
      }
      return userCredential;
    });
  }

  // Logout: Token, RefreshToken und photoURL entfernen
  logout(): Promise<void> {
    this.removeLocalStorageItem('authToken');
    this.removeLocalStorageItem('refreshToken');
    this.removeLocalStorageItem('photoURL');
    this.clearRoleCache();
    this.userRoleSubject.next(null);
    if (this.auth) {
      return this.auth.signOut();
    }
    return Promise.resolve();
  }
  isAdmin(): Observable<boolean> {
    return this.userRole$.pipe(
      map(role => role === 'admin'),
    );
  }
  // Get current user's role from the server with caching
  getUserRole(): Observable<UserRole | null> {
    const now = Date.now();

    // Cache zurücksetzen, wenn die Caching-Zeit abgelaufen ist oder kein Cache existiert
    if (!this.cachedUserRole$ || now - this.lastCacheTime > this.cacheDuration) {
      if (!this.getLocalStorageItem('authToken')) {
        return of(null);
      }
      this.lastCacheTime = now;
      let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
      this.cachedUserRole$ = this.http.get<{ role: UserRole | null }>(`${environment.apiBaseUrl}/bizmatch/auth/me/role`, { headers }).pipe(
        map(response => response.role),
        tap(role => this.userRoleSubject.next(role)),
        catchError(error => {
          console.error('Error fetching user role', error);
          return of(null);
        }),
        // Cache für mehrere Subscriber und behalte den letzten Wert
        // Der Parameter 1 gibt an, dass der letzte Wert gecacht werden soll
        // refCount: false bedeutet, dass der Cache nicht zurückgesetzt wird, wenn keine Subscriber mehr da sind
        shareReplay({ bufferSize: 1, refCount: false }),
      );
    }

    return this.cachedUserRole$;
  }
  clearRoleCache(): void {
    this.cachedUserRole$ = null;
    this.lastCacheTime = 0;
  }
  // Check if user has a specific role
  hasRole(role: UserRole): Observable<boolean> {
    return this.userRole$.pipe(
      map(userRole => {
        if (role === 'guest') {
          // Any authenticated user can access guest features
          return userRole !== null;
        } else if (role === 'pro') {
          // Both pro and admin can access pro features
          return userRole === 'pro' || userRole === 'admin';
        } else if (role === 'admin') {
          // Only admin can access admin features
          return userRole === 'admin';
        }
        return false;
      }),
    );
  }

  // Force refresh the token to get updated custom claims
  async refreshUserClaims(): Promise<void> {
    this.clearRoleCache();
    if (this.auth && this.auth.currentUser) {
      await this.auth.currentUser.getIdToken(true);
      const token = await this.auth.currentUser.getIdToken();
      this.setLocalStorageItem('authToken', token);
      this.loadRoleFromToken();
    }
  }
  // Prüft, ob ein Token noch gültig ist (über die "exp"-Eigenschaft)
  private isTokenValid(token: string): boolean {
    try {
      const payloadBase64 = token.split('.')[1];
      const payloadJson = atob(payloadBase64.replace(/-/g, '+').replace(/_/g, '/'));
      const payload = JSON.parse(payloadJson);
      const exp = payload.exp;
      const now = Math.floor(Date.now() / 1000);
      return exp > now;
    } catch (e) {
      return false;
    }
  }
  private isEMailVerified(token: string): boolean {
    try {
      const payloadBase64 = token.split('.')[1];
      const payloadJson = atob(payloadBase64.replace(/-/g, '+').replace(/_/g, '/'));
      const payload = JSON.parse(payloadJson);
      return payload.email_verified;
    } catch (e) {
      return false;
    }
  }
  // Versucht, mit dem RefreshToken einen neuen Access Token zu erhalten
  async refreshToken(): Promise<string | null> {
    const storedRefreshToken = this.getLocalStorageItem('refreshToken');
    // SSR protection: refreshToken should only run in browser
    if (!this.isBrowser) {
      return null;
    }

    if (!storedRefreshToken) {
      return null;
    }
    const apiKey = environment.firebaseConfig.apiKey; // Stelle sicher, dass dieser Wert in Deiner environment.ts gesetzt ist
    const url = `https://securetoken.googleapis.com/v1/token?key=${apiKey}`;

    const body = new HttpParams().set('grant_type', 'refresh_token').set('refresh_token', storedRefreshToken);

    const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' });

    try {
      const response: any = await firstValueFrom(this.http.post(url, body.toString(), { headers }));
      // response enthält z. B. id_token, refresh_token, expires_in etc.
      const newToken = response.id_token;
      const newRefreshToken = response.refresh_token;
      this.setLocalStorageItem('authToken', newToken);
      this.setLocalStorageItem('refreshToken', newRefreshToken);
      return newToken;
    } catch (error) {
      console.error('Error refreshing token:', error);
      return null;
    }
  }

  /**
   * Gibt einen gültigen Token zurück.
   * Falls der gespeicherte Token noch gültig ist, wird er zurückgegeben.
   * Ansonsten wird versucht, einen neuen Token mit dem RefreshToken zu holen.
   * Ist auch das nicht möglich, wird null zurückgegeben.
   */
  async getToken(): Promise<string | null> {
    const token = this.getLocalStorageItem('authToken');
    // SSR protection: return null on server
    if (!this.isBrowser) {
      return null;
    }

    if (token && !this.isEMailVerified(token)) {
      return null;
    } else if (token && this.isTokenValid(token) && this.isEMailVerified(token)) {
      return token;
    } else {
      return await this.refreshToken();
    }
  }

  // Add this new method to sign in with a custom token
  async signInWithCustomToken(token: string): Promise<void> {
    if (!this.isBrowser || !this.auth) {
      throw new Error('Auth is only available in browser context');
    }
    try {
      // Sign in to Firebase with the custom token
      const userCredential = await signInWithCustomToken(this.auth, token);

      // Store the authentication token
      if (userCredential.user) {
        const idToken = await userCredential.user.getIdToken();
        this.setLocalStorageItem('authToken', idToken);
        this.setLocalStorageItem('refreshToken', userCredential.user.refreshToken);

        if (userCredential.user.photoURL) {
          this.setLocalStorageItem('photoURL', userCredential.user.photoURL);
        }

        // Load user role from the token
        this.loadRoleFromToken();
      }

      return;
    } catch (error) {
      console.error('Error signing in with custom token:', error);
      throw error;
    }
  }
}
