/**
 * Token Extractor
 * 
 * Extracts authentication tokens from the Filevine page context.
 * Uses multiple strategies to find tokens in order of reliability.
 */

export class TokenExtractor {
  constructor() {
    this.listeners = new Set();
    this.interceptedTokens = null;
  }

  /**
   * Set up interception of network responses
   * This is the most reliable method to capture fresh tokens
   */
  setupInterception() {
    // We need to inject a script into the page context to intercept XHR/fetch
    // Content scripts run in an isolated world, so we use postMessage
    
    const script = document.createElement('script');
    script.textContent = `
      (function() {
        const originalFetch = window.fetch;
        
        window.fetch = async function(...args) {
          const response = await originalFetch.apply(this, args);
          
          // Check if this is a token response
          const url = typeof args[0] === 'string' ? args[0] : args[0]?.url;
          
          if (url && (url.includes('connect/token') || url.includes('identity.filevine.com'))) {
            try {
              const clone = response.clone();
              const data = await clone.json();
              
              if (data.access_token) {
                window.postMessage({
                  type: 'FV_PLUGIN_TOKEN',
                  token: data.access_token,
                  expiresIn: data.expires_in
                }, '*');
              }
            } catch (e) {
              // Ignore JSON parse errors
            }
          }
          
          return response;
        };
        
        // Also intercept XMLHttpRequest
        const originalOpen = XMLHttpRequest.prototype.open;
        const originalSend = XMLHttpRequest.prototype.send;
        
        XMLHttpRequest.prototype.open = function(method, url, ...rest) {
          this._fvUrl = url;
          return originalOpen.call(this, method, url, ...rest);
        };
        
        XMLHttpRequest.prototype.send = function(...args) {
          if (this._fvUrl && (this._fvUrl.includes('connect/token') || this._fvUrl.includes('identity.filevine.com'))) {
            this.addEventListener('load', function() {
              try {
                const data = JSON.parse(this.responseText);
                if (data.access_token) {
                  window.postMessage({
                    type: 'FV_PLUGIN_TOKEN',
                    token: data.access_token,
                    expiresIn: data.expires_in
                  }, '*');
                }
              } catch (e) {
                // Ignore
              }
            });
          }
          return originalSend.apply(this, args);
        };
        
        console.log('[Filevine Plugin] Network interception active');
      })();
    `;
    
    document.documentElement.appendChild(script);
    script.remove(); // Clean up after execution

    // Listen for intercepted tokens
    window.addEventListener('message', (event) => {
      if (event.source !== window) return;
      if (event.data?.type !== 'FV_PLUGIN_TOKEN') return;
      
      console.log('[Filevine Plugin] Intercepted token from network');
      this.handleInterceptedToken(event.data.token, event.data.expiresIn);
    });
  }

  /**
   * Handle an intercepted token
   */
  handleInterceptedToken(accessToken, expiresIn) {
    const orgId = this.extractOrgIdFromPage();
    
    this.interceptedTokens = {
      accessToken,
      orgId,
      expiresIn
    };

    // Notify listeners
    this.listeners.forEach(callback => {
      try {
        callback(this.interceptedTokens);
      } catch (e) {
        console.error('[Filevine Plugin] Token listener error:', e);
      }
    });
  }

  /**
   * Extract tokens from various page sources
   */
  async extractTokens() {
    // If we have intercepted tokens, use those (most reliable)
    if (this.interceptedTokens) {
      return this.interceptedTokens;
    }

    // Try localStorage first
    const storageTokens = this.extractFromStorage();
    if (storageTokens?.accessToken) {
      return storageTokens;
    }

    // Try to find in page state (React/Redux devtools pattern)
    const pageTokens = this.extractFromPageState();
    if (pageTokens?.accessToken) {
      return pageTokens;
    }

    // Last resort: check for OIDC patterns
    const oidcTokens = this.extractFromOidc();
    if (oidcTokens?.accessToken) {
      return oidcTokens;
    }

    return null;
  }

  /**
   * Extract tokens from localStorage/sessionStorage
   */
  extractFromStorage() {
    // Filevine may use various storage patterns
    const possibleKeys = [
      'fv_access_token',
      'access_token',
      'fv_auth',
      'auth',
      'oidc.user',
      'token'
    ];

    for (const storage of [localStorage, sessionStorage]) {
      for (const key of possibleKeys) {
        try {
          const value = storage.getItem(key);
          if (!value) continue;

          // Try to parse as JSON first
          try {
            const parsed = JSON.parse(value);
            if (parsed.access_token) {
              return {
                accessToken: parsed.access_token,
                orgId: parsed.org_id || parsed.orgId || this.extractOrgIdFromPage()
              };
            }
            if (parsed.accessToken) {
              return {
                accessToken: parsed.accessToken,
                orgId: parsed.orgId || this.extractOrgIdFromPage()
              };
            }
          } catch {
            // Not JSON, check if it looks like a JWT
            if (this.isJwtFormat(value)) {
              return {
                accessToken: value,
                orgId: this.extractOrgIdFromPage()
              };
            }
          }
        } catch (e) {
          // Storage access may fail in some contexts
        }
      }

      // Also search for OIDC-style keys
      try {
        for (let i = 0; i < storage.length; i++) {
          const key = storage.key(i);
          if (key?.includes('oidc.user') || key?.includes('filevine')) {
            const value = storage.getItem(key);
            if (value) {
              try {
                const parsed = JSON.parse(value);
                if (parsed.access_token) {
                  return {
                    accessToken: parsed.access_token,
                    orgId: this.extractOrgIdFromPage()
                  };
                }
              } catch {}
            }
          }
        }
      } catch {}
    }

    return null;
  }

  /**
   * Try to extract from page JavaScript state
   */
  extractFromPageState() {
    // This is less reliable but worth trying
    // Check window.__FILEVINE_STATE__ or similar patterns
    
    const statePatterns = [
      '__FILEVINE_STATE__',
      '__APP_STATE__',
      '__REDUX_STATE__',
      'filevineConfig',
      'appConfig'
    ];

    for (const pattern of statePatterns) {
      try {
        const state = window[pattern];
        if (state?.auth?.accessToken) {
          return {
            accessToken: state.auth.accessToken,
            orgId: state.auth.orgId || state.org?.id || this.extractOrgIdFromPage()
          };
        }
      } catch {}
    }

    return null;
  }

  /**
   * Extract from OIDC library patterns
   */
  extractFromOidc() {
    // oidc-client-js uses a specific localStorage pattern
    try {
      for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        if (key?.startsWith('oidc.user:')) {
          const value = localStorage.getItem(key);
          const parsed = JSON.parse(value);
          if (parsed?.access_token) {
            return {
              accessToken: parsed.access_token,
              orgId: this.extractOrgIdFromPage()
            };
          }
        }
      }
    } catch {}
    
    return null;
  }

  /**
   * Extract org ID from page URL or content
   */
  extractOrgIdFromPage() {
    // Try URL parameters
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.has('orgId')) {
      return urlParams.get('orgId');
    }

    // Try path patterns (e.g., /org/12345/projects)
    const pathMatch = window.location.pathname.match(/\/org\/(\d+)/);
    if (pathMatch) {
      return pathMatch[1];
    }

    // Check for org in subdomain
    const subdomainMatch = window.location.hostname.match(/^([^.]+)\.filevineapp\.com/);
    if (subdomainMatch && subdomainMatch[1] !== 'app') {
      // Could be org identifier
    }

    // Return null - will be fetched from API later
    return null;
  }

  /**
   * Check if string looks like a JWT
   */
  isJwtFormat(str) {
    if (!str || typeof str !== 'string') return false;
    const parts = str.split('.');
    return parts.length === 3 && parts.every(p => p.length > 0);
  }

  /**
   * Register callback for token events
   */
  onTokenReceived(callback) {
    this.listeners.add(callback);
    return () => this.listeners.delete(callback);
  }
}
