import { HttpClient } from '@angular/common/http';
import { inject, Injectable, PLATFORM_ID } from '@angular/core';
import { BehaviorSubject, map, Observable } from 'rxjs';
import { environment } from '../../environments/environment';
import { isPlatformBrowser } from '@angular/common';
import { Router } from '@angular/router';

interface ValidateOtpResponse {
  accessToken: string;
}

interface RefreshTokenResponse {
  accessToken: string;
}

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private readonly baseUrl = (environment.apiUrl ?? '').replace(/\/+$/, '');
  private accessToken: string | null = null;
  private platformId = inject(PLATFORM_ID);
  public accessTokenSub = new BehaviorSubject<string | null>(null);
  http = inject(HttpClient);
  router = inject(Router);

  private readonly storageKey = 'accessToken';
  tokenReady$ = new BehaviorSubject<boolean | null>(null);

  constructor() {
    this.accessToken = this.safeGetToken();
  }

  private api(path: string) {
    return `${this.baseUrl}${path.startsWith('/') ? '' : '/'}${path}`;
  }

  // Call on app start, or from guard
  async ensureTokenReady(): Promise<void>{
    if(this.tokenReady$.value) return;

    const stored = this.safeGetToken();

    // try to restore from storage
    if(stored){
      this.accessTokenSub.next(stored);
      this.tokenReady$.next(true);
      return;
    }
  }

  get currentToken(): string | null {
    return this.safeGetToken();
  }

  safeSetToken(token: string) {
    this.accessToken = token;

    if (isPlatformBrowser(this.platformId)) {
      localStorage.setItem(this.storageKey, token);
      this.accessTokenSub.next(token);
    }
  }

  private safeGetToken(): string | null {
    try {
      if (isPlatformBrowser(this.platformId)) {
        const token = localStorage.getItem(this.storageKey);
        this.accessTokenSub.next(token);
        return token;
      }
    } catch (e) {
      console.warn('Failed to read from localStorage:', e);
    }
    return null;
  }

  private safeRemoveToken() {
    this.accessToken = null;

    if (isPlatformBrowser(this.platformId)) {
      localStorage.removeItem(this.storageKey);
      this.accessTokenSub.next(null);
    }
  }

  sendOtp(email: string): Observable<unknown> {
    const formData = new FormData();
    formData.append('email', email);
    return this.http.post(this.api('/api/v1/auth/GenerateOtp'), formData);
  }

  verifyOtp(userId: string, otpCode: string): Observable<void> {
    const body = {
      UserId: userId,
      OtpCode: otpCode
    };
    return this.http.post<ValidateOtpResponse>(this.api('/api/v1/auth/ValidateOtp'), body).pipe(map((response: ValidateOtpResponse) => {
      if (response && response.accessToken) {
        this.accessToken = response.accessToken;
        this.safeSetToken(response.accessToken);
      }
    }));
  }

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

  logout(): void {
    this.http.post<void>(this.api('/api/v1/auth/logout'), {}).subscribe();
    this.accessToken = null;
    this.safeRemoveToken();
    this.router.navigate(['/admin/login']);
  }

  refreshToken(): Observable<RefreshTokenResponse> {
    return this.http.post<RefreshTokenResponse>(this.api('/api/v1/auth/RefreshToken'), {});
  }

  getApiKey(): string {
    return environment.apiKey;
  }

  isLoggedIn(): boolean {
    return this.safeGetToken() != null;
  }
}