import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { TokenPair } from '../models/auth.models';

@Injectable({
  providedIn: 'root'
})
export class TokenService {
  private readonly ACCESS_TOKEN_KEY = 'auth_access_token';
  private readonly REFRESH_TOKEN_KEY = 'auth_refresh_token';
  private readonly TOKEN_TYPE_KEY = 'auth_token_type';
  private readonly EXPIRES_AT_KEY = 'auth_expires_at';

  private tokenSubject = new BehaviorSubject<string | null>(this.getAccessToken());
  public token$ = this.tokenSubject.asObservable();

  constructor() {
    // Check token validity on service initialization
    this.checkTokenValidity();
  }

  /**
   * Store token pair in localStorage
   */
  setTokens(tokenPair: TokenPair): void {
    const expiresAt = Date.now() + (tokenPair.expires_in * 1000);
    
    localStorage.setItem(this.ACCESS_TOKEN_KEY, tokenPair.access_token);
    localStorage.setItem(this.REFRESH_TOKEN_KEY, tokenPair.refresh_token);
    localStorage.setItem(this.TOKEN_TYPE_KEY, tokenPair.token_type);
    localStorage.setItem(this.EXPIRES_AT_KEY, expiresAt.toString());
    
    this.tokenSubject.next(tokenPair.access_token);
  }

  /**
   * Get access token from localStorage
   */
  getAccessToken(): string | null {
    return localStorage.getItem(this.ACCESS_TOKEN_KEY);
  }

  /**
   * Get refresh token from localStorage
   */
  getRefreshToken(): string | null {
    return localStorage.getItem(this.REFRESH_TOKEN_KEY);
  }

  /**
   * Get token type from localStorage
   */
  getTokenType(): string {
    return localStorage.getItem(this.TOKEN_TYPE_KEY) || 'Bearer';
  }

  /**
   * Get token expiration timestamp
   */
  getExpiresAt(): number | null {
    const expiresAt = localStorage.getItem(this.EXPIRES_AT_KEY);
    return expiresAt ? parseInt(expiresAt, 10) : null;
  }

  /**
   * Check if token exists
   */
  hasToken(): boolean {
    return this.getAccessToken() !== null;
  }

  /**
   * Check if token is expired
   */
  isTokenExpired(): boolean {
    const expiresAt = this.getExpiresAt();
    if (!expiresAt) return true;
    
    // Add 30 second buffer to account for clock skew
    return Date.now() >= (expiresAt - 30000);
  }

  /**
   * Check if token is valid (exists and not expired)
   */
  isTokenValid(): boolean {
    return this.hasToken() && !this.isTokenExpired();
  }

  /**
   * Get authorization header value
   */
  getAuthorizationHeader(): string | null {
    const token = this.getAccessToken();
    const tokenType = this.getTokenType();
    
    return token ? `${tokenType} ${token}` : null;
  }

  /**
   * Clear all tokens from storage
   */
  clearTokens(): void {
    localStorage.removeItem(this.ACCESS_TOKEN_KEY);
    localStorage.removeItem(this.REFRESH_TOKEN_KEY);
    localStorage.removeItem(this.TOKEN_TYPE_KEY);
    localStorage.removeItem(this.EXPIRES_AT_KEY);
    
    this.tokenSubject.next(null);
  }

  /**
   * Get time until token expires (in milliseconds)
   */
  getTimeUntilExpiry(): number {
    const expiresAt = this.getExpiresAt();
    if (!expiresAt) return 0;
    
    const timeLeft = expiresAt - Date.now();
    return Math.max(0, timeLeft);
  }

  /**
   * Check if token will expire soon (within 5 minutes)
   */
  isTokenExpiringSoon(): boolean {
    const timeLeft = this.getTimeUntilExpiry();
    return timeLeft > 0 && timeLeft < 5 * 60 * 1000; // 5 minutes
  }

  /**
   * Decode JWT token payload (without verification)
   */
  decodeToken(token?: string): any {
    const tokenToDecoded = token || this.getAccessToken();
    if (!tokenToDecoded) return null;

    try {
      const payload = tokenToDecoded.split('.')[1];
      const decoded = atob(payload);
      return JSON.parse(decoded);
    } catch (error) {
      console.warn('Failed to decode token:', error);
      return null;
    }
  }

  /**
   * Get user ID from token
   */
  getUserId(): string | null {
    const payload = this.decodeToken();
    return payload?.sub || null;
  }

  /**
   * Get user email from token
   */
  getUserEmail(): string | null {
    const payload = this.decodeToken();
    return payload?.email || null;
  }

  /**
   * Get user scopes from token
   */
  getUserScopes(): string[] {
    const payload = this.decodeToken();
    return payload?.scopes || [];
  }

  /**
   * Get user organization from token
   */
  getUserOrganization(): string | null {
    const payload = this.decodeToken();
    return payload?.organization || null;
  }

  /**
   * Check if user has specific scope
   */
  hasScope(scope: string): boolean {
    const scopes = this.getUserScopes();
    return scopes.includes(scope);
  }

  /**
   * Check if user has any of the specified scopes
   */
  hasAnyScope(scopes: string[]): boolean {
    const userScopes = this.getUserScopes();
    return scopes.some(scope => userScopes.includes(scope));
  }

  /**
   * Check if user has all of the specified scopes
   */
  hasAllScopes(scopes: string[]): boolean {
    const userScopes = this.getUserScopes();
    return scopes.every(scope => userScopes.includes(scope));
  }

  /**
   * Check token validity and clear if invalid
   */
  private checkTokenValidity(): void {
    if (this.hasToken() && this.isTokenExpired()) {
      this.clearTokens();
    }
  }

  /**
   * Subscribe to storage events for cross-tab synchronization
   */
  initStorageListener(): void {
    window.addEventListener('storage', (event) => {
      if (event.key === this.ACCESS_TOKEN_KEY) {
        this.tokenSubject.next(event.newValue);
      } else if (event.key === null) {
        // Storage was cleared
        this.tokenSubject.next(null);
      }
    });
  }
}