/**
 * Authentication Manager
 * 
 * Handles token storage, validation, and refresh for Filevine API access.
 * 
 * CRITICAL: The Filevine API requires:
 * 1. access_token - Bearer token from OAuth
 * 2. userId - NUMERIC ID (not UUID from JWT sub claim!)
 * 3. orgId - Organization ID
 * 4. sessionId - JWT 'jti' claim
 */

import { STORAGE_KEYS, API_BASE_URL, ENDPOINTS, MESSAGE_TYPES } from '../lib/constants.js';
import { filevineClient } from './api.js';

class AuthManager {
  constructor() {
    this.auth = null;
    this.listeners = new Set();
  }

  /**
   * Initialize auth from storage on extension load
   */
  async initialize() {
    const stored = await chrome.storage.session.get(STORAGE_KEYS.AUTH);
    if (stored[STORAGE_KEYS.AUTH]) {
      this.auth = stored[STORAGE_KEYS.AUTH];
      
      // Check if token is expired
      if (this.isExpired()) {
        await this.clearAuth();
        return null;
      }
      
      // Initialize API client
      filevineClient.setCredentials(this.auth);
      return this.auth;
    }
    return null;
  }

  /**
   * Parse JWT token to extract claims
   */
  parseJwt(token) {
    try {
      const parts = token.split('.');
      if (parts.length !== 3) {
        throw new Error('Invalid JWT format');
      }
      
      // Decode payload (part 1)
      let payload = parts[1];
      // Add padding for base64 decoding
      const padding = 4 - (payload.length % 4);
      if (padding !== 4) {
        payload += '='.repeat(padding);
      }
      
      const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
      return JSON.parse(decoded);
    } catch (error) {
      console.error('Failed to parse JWT:', error);
      return null;
    }
  }

  /**
   * Process raw tokens received from content script
   * Completes the auth flow by getting numeric user ID
   */
  async processTokens({ accessToken, orgId }) {
    if (!accessToken) {
      throw new Error('No access token provided');
    }

    // Step 1: Parse JWT to get session ID (jti claim)
    const claims = this.parseJwt(accessToken);
    if (!claims) {
      throw new Error('Failed to parse access token');
    }
    
    const sessionId = claims.jti;
    const expiresAt = claims.exp ? claims.exp * 1000 : Date.now() + 3600000; // 1 hour default

    // Step 2: Get NUMERIC user ID from API
    // CRITICAL: We cannot use the 'sub' claim from JWT - must call GetUserOrgsWithToken
    const url = `${API_BASE_URL}${ENDPOINTS.GET_USER_ORGS}`;
    console.log('[Auth] Calling GetUserOrgsWithToken:', url);
    console.log('[Auth] Token preview:', accessToken.substring(0, 50) + '...');
    
    const userOrgResponse = await fetch(url, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      }
    });

    console.log('[Auth] Response status:', userOrgResponse.status, userOrgResponse.statusText);

    if (!userOrgResponse.ok) {
      const errorText = await userOrgResponse.text();
      console.error('[Auth] API Error response:', errorText);
      throw new Error(`Failed to get user info (${userOrgResponse.status}): ${errorText}`);
    }

    const userOrgData = await userOrgResponse.json();
    
    // Extract NUMERIC user ID (not UUID!)
    const userId = String(userOrgData.user.userId.native);
    
    // If no orgId provided, use first available org
    const finalOrgId = orgId || String(userOrgData.orgs[0]?.orgId);

    // Build complete auth object
    this.auth = {
      accessToken,
      userId,       // NUMERIC
      orgId: finalOrgId,
      sessionId,    // JWT jti
      expiresAt,
      orgs: userOrgData.orgs, // Store all orgs for multi-org support
    };

    // Persist to session storage
    await chrome.storage.session.set({
      [STORAGE_KEYS.AUTH]: this.auth
    });

    // Initialize API client
    filevineClient.setCredentials(this.auth);

    // Notify listeners
    this.notifyListeners('authenticated');

    return this.auth;
  }

  /**
   * Process cookie-based authentication
   * Uses web app API with cookies instead of Bearer tokens
   */
  async processCookieAuth({ orgId, orgName, userId, cookie }) {
    console.log('[Auth] Processing cookie-based auth...');
    
    // Get user info from web app API
    // Try multiple endpoints - web app API might use different endpoints
    try {
      // Try /api/currentuser/setting/uiSettings first (we saw this in network logs)
      let userResponse = await fetch('https://product.filevineapp.com/api/currentuser/setting/uiSettings', {
        method: 'GET',
        headers: {
          'Cookie': cookie,
          'Content-Type': 'application/json',
          'Accept': 'application/json'
        },
        credentials: 'include'
      });

      // Check if response is JSON
      const contentType = userResponse.headers.get('content-type');
      if (!contentType || !contentType.includes('application/json')) {
        console.log('[Auth] /api/currentuser/setting/uiSettings returned non-JSON, trying /api/orgs...');
        
        // Try /api/orgs - we know this works and returns org info with ownerID
        userResponse = await fetch('https://product.filevineapp.com/api/orgs', {
          method: 'GET',
          headers: {
            'Cookie': cookie,
            'Content-Type': 'application/json',
            'Accept': 'application/json'
          },
          credentials: 'include'
        });
      }

      if (!userResponse.ok) {
        const errorText = await userResponse.text();
        console.error('[Auth] API error response:', errorText.substring(0, 200));
        throw new Error(`Failed to get user info: ${userResponse.status}`);
      }

      const userData = await userResponse.json();
      console.log('[Auth] User data:', JSON.stringify(userData).substring(0, 200));
      
      // Extract user ID from response
      // The /api/currentuser/setting/uiSettings returns settings, not user info
      // We need to get user ID from /api/orgs instead
      let userId = 'unknown';
      
      // If we got settings data, fetch orgs to get user ID
      if (userData.data && typeof userData.data === 'string') {
        // This is settings data, get user ID from orgs
        console.log('[Auth] Got settings data, fetching orgs for user ID...');
        const orgsResponse = await fetch('https://product.filevineapp.com/api/orgs', {
          method: 'GET',
          headers: {
            'Cookie': cookie,
            'Content-Type': 'application/json',
            'Accept': 'application/json'
          },
          credentials: 'include'
        });
        
        if (orgsResponse.ok) {
          const orgsData = await orgsResponse.json();
          if (orgsData.data && Array.isArray(orgsData.data) && orgsData.data.length > 0) {
            userId = String(orgsData.data[0].ownerID || orgsData.data[0].ownerId || 'unknown');
            console.log('[Auth] Got user ID from orgs:', userId);
          }
        }
      } else if (userData.data && Array.isArray(userData.data) && userData.data.length > 0) {
        // From orgs endpoint - use ownerID
        userId = String(userData.data[0].ownerID || userData.data[0].ownerId || 'unknown');
        console.log('[Auth] Got user ID from orgs response:', userId);
      } else if (userData.data?.userId) {
        userId = String(userData.data.userId);
      } else if (userData.data?.id) {
        userId = String(userData.data.id);
      } else if (userData.userId) {
        userId = String(userData.userId);
      }
      
      // Use provided userId if available, otherwise try to extract it
      const finalUserId = userId && userId !== 'unknown' 
        ? String(userId) 
        : (userId !== 'unknown' ? String(userId) : 'unknown');
      
      if (finalUserId === 'unknown') {
        console.warn('[Auth] Could not determine user ID');
      } else {
        console.log('[Auth] Using user ID:', finalUserId);
      }
      
      // Build orgs array with current org
      const orgs = [{
        orgId: orgId,
        name: orgName || `Org ${orgId}`,
        ownerID: finalUserId
      }];

      // Build auth object using cookies instead of Bearer tokens
      this.auth = {
        accessToken: null, // No Bearer token - using cookies
        cookie: cookie,    // Store cookie for API calls
        userId: finalUserId,
        orgId: orgId,
        orgName: orgName,
        orgs: orgs,        // Include orgs array for multi-org selector
        sessionId: 'cookie-session', // Placeholder
        expiresAt: Date.now() + 3600000, // 1 hour default
        usingWebApi: true, // Flag to use web app API
      };

      // Persist to session storage
      await chrome.storage.session.set({
        [STORAGE_KEYS.AUTH]: this.auth
      });

      // Notify listeners
      this.notifyListeners('authenticated');

      console.log('[Auth] Cookie auth successful!');
      console.log('[Auth] Auth object:', {
        userId: this.auth.userId,
        orgId: this.auth.orgId,
        usingWebApi: this.auth.usingWebApi,
        hasCookie: !!this.auth.cookie
      });
      return this.auth;
    } catch (error) {
      console.error('[Auth] Cookie auth error:', error);
      throw error;
    }
  }

  /**
   * Check if current auth is expired
   */
  isExpired() {
    if (!this.auth || !this.auth.expiresAt) return true;
    // Consider expired 5 minutes before actual expiry
    return Date.now() > (this.auth.expiresAt - 300000);
  }

  /**
   * Check if we have valid authentication
   */
  isAuthenticated() {
    return this.auth && !this.isExpired();
  }

  /**
   * Get current auth state
   */
  getState() {
    if (!this.auth) {
      return { authenticated: false, state: 'disconnected' };
    }
    
    if (this.isExpired()) {
      return { authenticated: false, state: 'expired', auth: this.auth };
    }
    
    return { 
      authenticated: true, 
      state: 'connected',
      auth: {
        userId: this.auth.userId,
        orgId: this.auth.orgId,
        orgs: this.auth.orgs,
        expiresAt: this.auth.expiresAt,
        usingWebApi: this.auth.usingWebApi, // Include this flag!
        cookie: this.auth.cookie // Include cookie for web API
      }
    };
  }

  /**
   * Switch to a different organization (for multi-org users)
   */
  async switchOrg(orgId) {
    if (!this.auth) {
      throw new Error('Not authenticated');
    }
    
    // Verify org is available to user
    const org = this.auth.orgs?.find(o => String(o.orgId) === String(orgId));
    if (!org) {
      throw new Error('Organization not available');
    }
    
    this.auth.orgId = String(orgId);
    
    // Update storage
    await chrome.storage.session.set({
      [STORAGE_KEYS.AUTH]: this.auth
    });
    
    // Update API client
    filevineClient.setCredentials(this.auth);
    
    this.notifyListeners('org_changed');
    
    return this.auth;
  }

  /**
   * Clear authentication
   */
  async clearAuth() {
    this.auth = null;
    await chrome.storage.session.remove(STORAGE_KEYS.AUTH);
    this.notifyListeners('logged_out');
  }

  /**
   * Add listener for auth state changes
   */
  addListener(callback) {
    this.listeners.add(callback);
    return () => this.listeners.delete(callback);
  }

  /**
   * Notify all listeners of auth change
   */
  notifyListeners(event) {
    const state = this.getState();
    this.listeners.forEach(callback => {
      try {
        callback(event, state);
      } catch (error) {
        console.error('Auth listener error:', error);
      }
    });
    
    // Broadcast to all extension contexts
    chrome.runtime.sendMessage({
      type: MESSAGE_TYPES.AUTH_STATE_CHANGED,
      event,
      state
    }).catch(() => {
      // Ignore errors when no listeners
    });
  }
}

// Singleton instance
export const authManager = new AuthManager();
