/**
 * TextMe Channel Plugin - Authentication Manager
 * 
 * Handles token storage, refresh, and multi-account authentication.
 */

import {
  TextMeAPI,
  type AuthTokenResponse,
  type AuthTokenRefreshResponse,
} from 'textme-unofficial-api';
import {
  ConfigManager,
  type AccountConfig,
  type StoredTokens,
} from './config.js';

// ============================================================================
// Types
// ============================================================================

export interface AuthenticatedAccount {
  account: AccountConfig;
  tokens: StoredTokens;
  api: TextMeAPI;
  userId?: number;
}

export interface AuthManagerOptions {
  /** Token refresh buffer (ms before expiry to refresh) */
  refreshBuffer?: number;
  /** Auto-refresh interval (ms) */
  autoRefreshInterval?: number;
  /** Enable debug logging */
  debug?: boolean;
}

// ============================================================================
// Auth Manager
// ============================================================================

export class AuthManager {
  private configManager: ConfigManager;
  private authenticatedAccounts: Map<string, AuthenticatedAccount> = new Map();
  private refreshTimers: Map<string, NodeJS.Timeout> = new Map();
  private options: Required<AuthManagerOptions>;

  constructor(configManager: ConfigManager, options: AuthManagerOptions = {}) {
    this.configManager = configManager;
    this.options = {
      refreshBuffer: options.refreshBuffer ?? 5 * 60 * 1000, // 5 minutes
      autoRefreshInterval: options.autoRefreshInterval ?? 30 * 60 * 1000, // 30 minutes
      debug: options.debug ?? false,
    };
  }

  /**
   * Initialize authentication for all enabled accounts
   */
  async initialize(): Promise<AuthenticatedAccount[]> {
    const accounts = this.configManager.getEnabledAccounts();
    const results: AuthenticatedAccount[] = [];

    for (const account of accounts) {
      try {
        const authenticated = await this.authenticateAccount(account);
        results.push(authenticated);
        this.log(`Account ${account.id} authenticated successfully`);
      } catch (err) {
        console.error(`[TextMeAuth] Failed to authenticate account ${account.id}:`, err);
      }
    }

    if (results.length === 0) {
      throw new Error('[TextMeAuth] No accounts authenticated successfully');
    }

    // Start auto-refresh timers
    this.startAutoRefresh();

    return results;
  }

  /**
   * Authenticate a single account
   */
  async authenticateAccount(account: AccountConfig): Promise<AuthenticatedAccount> {
    // Check for stored tokens first
    const storedTokens = this.configManager.getStoredTokens(account.id);

    if (storedTokens && !this.configManager.isTokenExpired(account.id, this.options.refreshBuffer)) {
      this.log(`Using stored tokens for account ${account.id}`);
      
      // Create API client with stored tokens
      const api = new TextMeAPI({ Authorization: `Bearer ${storedTokens.accessToken}` });
      
      const authenticated: AuthenticatedAccount = {
        account,
        tokens: storedTokens,
        api,
        userId: storedTokens.userId,
      };

      this.authenticatedAccounts.set(account.id, authenticated);
      return authenticated;
    }

    // Try to refresh if we have a refresh token
    if (storedTokens?.refreshToken) {
      try {
        this.log(`Refreshing tokens for account ${account.id}`);
        return await this.refreshAccountTokens(account.id);
      } catch (err) {
        this.log(`Token refresh failed for ${account.id}, falling back to login`);
      }
    }

    // Fresh login
    return await this.loginAccount(account);
  }

  /**
   * Login with credentials
   */
  private async loginAccount(account: AccountConfig): Promise<AuthenticatedAccount> {
    this.log(`Logging in account ${account.id}`);

    const loginResponse: AuthTokenResponse = await TextMeAPI.login({
      email: account.credentials.email,
      password: account.credentials.password,
    });

    // Calculate expiry (default 24 hours if not provided)
    const expiresAt = Date.now() + (24 * 60 * 60 * 1000);

    const tokens: StoredTokens = {
      accessToken: loginResponse.access,
      refreshToken: loginResponse.refresh,
      expiresAt,
      userId: loginResponse.user_id,
    };

    // Store tokens
    await this.configManager.storeTokens(account.id, tokens);

    // Create API client
    const api = new TextMeAPI({ Authorization: `Bearer ${tokens.accessToken}` });

    const authenticated: AuthenticatedAccount = {
      account,
      tokens,
      api,
      userId: loginResponse.user_id,
    };

    this.authenticatedAccounts.set(account.id, authenticated);
    return authenticated;
  }

  /**
   * Refresh tokens for an account
   */
  async refreshAccountTokens(accountId: string): Promise<AuthenticatedAccount> {
    const existing = this.authenticatedAccounts.get(accountId);
    const storedTokens = this.configManager.getStoredTokens(accountId);
    const account = this.configManager.getAccountById(accountId);

    if (!account) {
      throw new Error(`Account ${accountId} not found`);
    }

    const refreshToken = existing?.tokens.refreshToken || storedTokens?.refreshToken;
    if (!refreshToken) {
      throw new Error(`No refresh token for account ${accountId}`);
    }

    this.log(`Refreshing tokens for account ${accountId}`);

    const refreshResponse: AuthTokenRefreshResponse = await TextMeAPI.refreshToken({
      refresh: refreshToken,
    });

    // Calculate new expiry
    const expiresAt = Date.now() + (24 * 60 * 60 * 1000);

    const tokens: StoredTokens = {
      accessToken: refreshResponse.access,
      refreshToken: refreshResponse.refresh || refreshToken,
      expiresAt,
      userId: existing?.userId || storedTokens?.userId,
    };

    // Store updated tokens
    await this.configManager.storeTokens(accountId, tokens);

    // Update or create API client
    const api = existing?.api || new TextMeAPI({ Authorization: `Bearer ${tokens.accessToken}` });
    api.setAuthHeaders({ Authorization: `Bearer ${tokens.accessToken}` });

    const authenticated: AuthenticatedAccount = {
      account,
      tokens,
      api,
      userId: tokens.userId,
    };

    this.authenticatedAccounts.set(accountId, authenticated);
    return authenticated;
  }

  /**
   * Start auto-refresh timers for all accounts
   */
  private startAutoRefresh(): void {
    // Clear existing timers
    this.stopAutoRefresh();

    for (const [accountId, auth] of this.authenticatedAccounts) {
      this.scheduleRefresh(accountId, auth.tokens);
    }
  }

  /**
   * Schedule token refresh for an account
   */
  private scheduleRefresh(accountId: string, tokens: StoredTokens): void {
    if (!tokens.expiresAt) {
      // No expiry known, use default interval
      const timer = setTimeout(
        () => this.handleRefresh(accountId),
        this.options.autoRefreshInterval
      );
      this.refreshTimers.set(accountId, timer);
      return;
    }

    // Calculate when to refresh (before expiry with buffer)
    const refreshAt = tokens.expiresAt - this.options.refreshBuffer;
    const delay = Math.max(refreshAt - Date.now(), 60000); // At least 1 minute

    this.log(`Scheduling refresh for ${accountId} in ${Math.round(delay / 1000)}s`);

    const timer = setTimeout(
      () => this.handleRefresh(accountId),
      delay
    );
    this.refreshTimers.set(accountId, timer);
  }

  /**
   * Handle scheduled token refresh
   */
  private async handleRefresh(accountId: string): Promise<void> {
    try {
      const auth = await this.refreshAccountTokens(accountId);
      this.scheduleRefresh(accountId, auth.tokens);
      this.log(`Successfully refreshed tokens for ${accountId}`);
    } catch (err) {
      console.error(`[TextMeAuth] Failed to refresh tokens for ${accountId}:`, err);
      
      // Try to re-authenticate
      const account = this.configManager.getAccountById(accountId);
      if (account) {
        try {
          const auth = await this.loginAccount(account);
          this.scheduleRefresh(accountId, auth.tokens);
          this.log(`Re-authenticated account ${accountId}`);
        } catch (loginErr) {
          console.error(`[TextMeAuth] Failed to re-authenticate ${accountId}:`, loginErr);
        }
      }
    }
  }

  /**
   * Stop all auto-refresh timers
   */
  stopAutoRefresh(): void {
    for (const timer of this.refreshTimers.values()) {
      clearTimeout(timer);
    }
    this.refreshTimers.clear();
  }

  /**
   * Get authenticated account by ID
   */
  getAccount(accountId: string): AuthenticatedAccount | undefined {
    return this.authenticatedAccounts.get(accountId);
  }

  /**
   * Get all authenticated accounts
   */
  getAllAccounts(): AuthenticatedAccount[] {
    return Array.from(this.authenticatedAccounts.values());
  }

  /**
   * Get authenticated account by phone number
   */
  getAccountByPhoneNumber(phoneNumber: string): AuthenticatedAccount | undefined {
    const accountConfig = this.configManager.getAccountByPhoneNumber(phoneNumber);
    if (!accountConfig) return undefined;
    return this.authenticatedAccounts.get(accountConfig.id);
  }

  /**
   * Get default authenticated account
   */
  getDefaultAccount(): AuthenticatedAccount | undefined {
    const defaultConfig = this.configManager.getDefaultAccount();
    if (!defaultConfig) return undefined;
    return this.authenticatedAccounts.get(defaultConfig.id);
  }

  /**
   * Logout account and remove tokens
   */
  async logoutAccount(accountId: string): Promise<void> {
    // Clear refresh timer
    const timer = this.refreshTimers.get(accountId);
    if (timer) {
      clearTimeout(timer);
      this.refreshTimers.delete(accountId);
    }

    // Remove from authenticated accounts
    this.authenticatedAccounts.delete(accountId);

    // Remove stored tokens
    await this.configManager.removeTokens(accountId);

    this.log(`Logged out account ${accountId}`);
  }

  /**
   * Shutdown auth manager
   */
  async shutdown(): Promise<void> {
    this.stopAutoRefresh();
    this.authenticatedAccounts.clear();
    this.log('Auth manager shutdown');
  }

  /**
   * Debug logging
   */
  private log(message: string): void {
    if (this.options.debug) {
      console.log(`[TextMeAuth] ${message}`);
    }
  }
}

// ============================================================================
// Factory
// ============================================================================

export function createAuthManager(
  configManager: ConfigManager,
  options?: AuthManagerOptions
): AuthManager {
  return new AuthManager(configManager, options);
}
