/**
 * Filevine API Client
 * 
 * Handles all API communication with Filevine.
 * 
 * CRITICAL IMPLEMENTATION NOTES (from testing):
 * 1. Base URL must be https://api.filevineapp.com/fv-app/v2 (NOT api.filevine.io)
 * 2. User ID must be NUMERIC from GetUserOrgsWithToken (NOT UUID from JWT)
 * 3. All endpoints use PascalCase (e.g., /Projects, /Contacts)
 * 4. All requests require three x-fv-* headers
 */

import { API_BASE_URL, ENDPOINTS, RATE_LIMITS } from '../lib/constants.js';

export class FilevineClient {
  constructor() {
    this.accessToken = null;
    this.userId = null;      // MUST be numeric
    this.orgId = null;
    this.sessionId = null;   // JWT jti claim
    this.requestQueue = [];
    this.lastRequestTime = 0;
  }

  /**
   * Initialize client with auth credentials
   */
  setCredentials({ accessToken, userId, orgId, sessionId }) {
    this.accessToken = accessToken;
    this.userId = userId;
    this.orgId = orgId;
    this.sessionId = sessionId;
  }

  /**
   * Check if client has valid credentials
   */
  isAuthenticated() {
    return !!(this.accessToken && this.userId && this.orgId && this.sessionId);
  }

  /**
   * Build required headers for API requests
   * CRITICAL: All three x-fv-* headers are required
   */
  getHeaders() {
    if (!this.isAuthenticated()) {
      throw new Error('Client not authenticated. Call setCredentials first.');
    }
    
    return {
      'Authorization': `Bearer ${this.accessToken}`,
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'x-fv-userid': String(this.userId),     // MUST be string of numeric ID
      'x-fv-orgid': String(this.orgId),
      'x-fv-sessionid': String(this.sessionId) // JWT jti claim
    };
  }

  /**
   * Make an API request with rate limiting and retry logic
   */
  async request(method, endpoint, { params = {}, data = null } = {}) {
    // Basic rate limiting
    const now = Date.now();
    const timeSinceLastRequest = now - this.lastRequestTime;
    const minInterval = 60000 / RATE_LIMITS.BURST_PER_MINUTE;
    
    if (timeSinceLastRequest < minInterval) {
      await new Promise(resolve => setTimeout(resolve, minInterval - timeSinceLastRequest));
    }
    
    this.lastRequestTime = Date.now();
    
    // Build URL with query params
    const url = new URL(`${API_BASE_URL}${endpoint}`);
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined && value !== null) {
        url.searchParams.append(key, value);
      }
    });
    
    // Make request with retry logic
    let lastError;
    for (let attempt = 0; attempt < RATE_LIMITS.MAX_RETRIES; attempt++) {
      try {
        const response = await fetch(url.toString(), {
          method,
          headers: this.getHeaders(),
          body: data ? JSON.stringify(data) : undefined
        });
        
        if (response.status === 429) {
          // Rate limited - wait and retry
          const retryAfter = response.headers.get('Retry-After') || RATE_LIMITS.RETRY_DELAY_MS;
          await new Promise(resolve => setTimeout(resolve, parseInt(retryAfter)));
          continue;
        }
        
        if (!response.ok) {
          const errorText = await response.text();
          throw new Error(`API Error (${response.status}): ${errorText}`);
        }
        
        // Handle empty responses
        const text = await response.text();
        return text ? JSON.parse(text) : null;
        
      } catch (error) {
        lastError = error;
        if (attempt < RATE_LIMITS.MAX_RETRIES - 1) {
          await new Promise(resolve => 
            setTimeout(resolve, RATE_LIMITS.RETRY_DELAY_MS * (attempt + 1))
          );
        }
      }
    }
    
    throw lastError;
  }

  // ============================================
  // Convenience methods for common operations
  // ============================================

  /**
   * Get list of projects accessible to user
   */
  async getProjects({ limit = 50, offset = 0, search = '' } = {}) {
    const params = { limit, offset };
    if (search) {
      params.q = search;
    }
    return this.request('GET', ENDPOINTS.PROJECTS, { params });
  }

  /**
   * Get single project by ID
   */
  async getProject(projectId) {
    return this.request('GET', ENDPOINTS.PROJECT(projectId));
  }

  /**
   * Get documents for a project
   */
  async getProjectDocuments(projectId, { limit = 100, offset = 0 } = {}) {
    return this.request('GET', ENDPOINTS.PROJECT_DOCS(projectId), {
      params: { limit, offset }
    });
  }

  /**
   * Get download URL for a document
   * Returns a presigned S3 URL
   */
  async getDocumentDownloadUrl(docId) {
    return this.request('GET', ENDPOINTS.DOC_DOWNLOAD(docId));
  }

  /**
   * Get project types
   */
  async getProjectTypes() {
    return this.request('GET', ENDPOINTS.PROJECT_TYPES);
  }
}

// Singleton instance for extension-wide use
export const filevineClient = new FilevineClient();
