import { API_CONFIG, getCredentials } from '../config/constants';

interface TokenResponse {
  access_token: string;
  expires_in: string;
  token_type: string;
  refresh_token: string;
  id_token: string;
  user_id: string;
  project_id: string;
}

export interface AuthStatus {
  isAuthenticated: boolean;
  expiresAt: number | null;
  expiresIn: number | null; // seconds until expiry
  accessToken: string | null; // full token
  lastError: string | null;
}

class AuthManager {
  private accessToken: string | null = null;
  private expiresAt: number = 0;
  private lastError: string | null = null;
  private listeners: Set<() => void> = new Set();
  private initPromise: Promise<void> | null = null;
  private isReady: boolean = false;

  // Ensure token is ready before any queries
  async ensureReady(): Promise<boolean> {
    if (this.isReady && this.accessToken) {
      return true;
    }

    if (!this.initPromise) {
      this.initPromise = this.refresh()
        .then(() => {
          this.isReady = true;
        })
        .catch((err) => {
          console.error('Initial token fetch failed:', err);
          this.isReady = false;
        });
    }

    await this.initPromise;
    return this.isReady;
  }

  async getToken(): Promise<string> {
    // Refresh if token expires in less than 1 minute
    if (!this.accessToken || Date.now() >= this.expiresAt - 60000) {
      await this.refresh();
    }
    return this.accessToken!;
  }

  private async refresh(): Promise<void> {
    const creds = getCredentials();
    const url = `${API_CONFIG.FIREBASE_TOKEN_URL}?key=${API_CONFIG.FIREBASE_API_KEY}`;
    console.log('Refreshing token at:', url);

    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          grantType: 'refresh_token',
          refreshToken: creds.REFRESH_TOKEN,
        }),
      });

      if (!response.ok) {
        const error = await response.text();
        console.error('Token refresh failed:', response.status, error);
        this.lastError = `HTTP ${response.status}: ${error}`;
        this.notifyListeners();
        throw new Error(`Failed to refresh token: ${error}`);
      }

      const data: TokenResponse = await response.json();
      this.accessToken = data.access_token;
      this.expiresAt = Date.now() + parseInt(data.expires_in) * 1000;
      this.lastError = null;

      console.log('Token refreshed, expires in:', data.expires_in, 'seconds');

      // Seed the backend with updated refresh token for rotation cron
      try {
        const creds = getCredentials();
        fetch('/api/location-rotation/seed-token', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            refreshToken: creds.REFRESH_TOKEN,
            profileId: creds.PROFILE_ID,
            analyticsId: creds.EVENT_ANALYTICS_ID,
          }),
        }).catch(() => {}); // Best-effort, don't block auth flow
      } catch (e) {}

      this.notifyListeners();
    } catch (err) {
      this.lastError = err instanceof Error ? err.message : 'Unknown error';
      this.notifyListeners();
      throw err;
    }
  }

  // Force a token refresh (useful after updating credentials)
  async forceRefresh(): Promise<void> {
    this.accessToken = null;
    this.expiresAt = 0;
    await this.refresh();
  }

  getProfileId(): string {
    return getCredentials().PROFILE_ID;
  }

  isAuthenticated(): boolean {
    return this.accessToken !== null && Date.now() < this.expiresAt;
  }

  getStatus(): AuthStatus {
    const now = Date.now();
    const expiresIn = this.expiresAt > 0 ? Math.floor((this.expiresAt - now) / 1000) : null;

    return {
      isAuthenticated: this.isAuthenticated(),
      expiresAt: this.expiresAt > 0 ? this.expiresAt : null,
      expiresIn: expiresIn && expiresIn > 0 ? expiresIn : null,
      accessToken: this.accessToken,
      lastError: this.lastError,
    };
  }

  // Subscribe to auth status changes
  subscribe(listener: () => void): () => void {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  }

  private notifyListeners() {
    this.listeners.forEach(listener => listener());
  }
}

export const authManager = new AuthManager();
