/**
 * Web App API Client
 * 
 * Client for Filevine's web app API (product.filevineapp.com/api/...)
 * Uses cookies instead of Bearer tokens.
 */

export class WebApiClient {
  constructor() {
    this.cookie = null;
    this.userId = null;
    this.orgId = null;
  }

  /**
   * Initialize client with cookie-based auth
   */
  setCredentials({ cookie, userId, orgId }) {
    this.cookie = cookie;
    this.userId = userId;
    this.orgId = orgId;
    console.log('[Web API] Credentials set, userId:', userId, 'orgId:', orgId, 'cookie length:', cookie?.length);
  }

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

  /**
   * Build headers for web app API requests
   */
  getHeaders() {
    if (!this.isAuthenticated()) {
      throw new Error('Client not authenticated. Call setCredentials first.');
    }
    
    return {
      'Cookie': this.cookie,
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    };
  }

  /**
   * Make a request to the web app API
   */
  async request(method, endpoint, { params = {}, data = null } = {}) {
    if (!this.isAuthenticated()) {
      throw new Error('Not authenticated');
    }

    // Build URL
    const baseUrl = 'https://product.filevineapp.com/api';
    const url = new URL(`${baseUrl}${endpoint}`);
    
    // Add query params (for GET requests)
    if (method === 'GET') {
      Object.entries(params).forEach(([key, value]) => {
        if (value !== undefined && value !== null) {
          url.searchParams.append(key, value);
        }
      });
    }
    
    // Prepare request options
    const requestOptions = {
      method,
      headers: this.getHeaders(),
      credentials: 'include'
    };
    
    // Add body for POST/PUT/PATCH requests
    if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
      requestOptions.body = JSON.stringify(data);
    }
    
    console.log('[Web API] Request:', method, url.toString(), data ? JSON.stringify(data).substring(0, 200) : '');
    
    // Make request
    const response = await fetch(url.toString(), requestOptions);
    
    if (!response.ok) {
      const errorText = await response.text();
      console.error('[Web API] Error response:', response.status, response.statusText, errorText.substring(0, 500));
      console.error('[Web API] Request URL was:', url.toString());
      console.error('[Web API] Request headers:', JSON.stringify(requestOptions.headers));
      throw new Error(`API Error (${response.status}): ${errorText.substring(0, 200)}`);
    }
    
    const text = await response.text();
    if (!text) {
      return null;
    }
    
    try {
      return JSON.parse(text);
    } catch (e) {
      console.error('[Web API] Failed to parse JSON:', text.substring(0, 200));
      throw new Error('Invalid JSON response');
    }
  }

  /**
   * Get projects (using web app API endpoint)
   */
  async getProjects({ limit = 50, offset = 0, search = '' } = {}) {
    console.log('[Web API] Getting projects, search:', search, 'orgId:', this.orgId, 'limit:', limit);
    
    // Use projectHub/search endpoint (POST with body)
    // Match the exact format Filevine uses
    try {
      const requestBody = {
        query: search || '',
        orgID: this.orgId ? parseInt(this.orgId) : undefined,
        filterByMinimumAccessLevel: 2,
        filterByProjectTypeID: null,
        direction: 'Descending',
        filterByFirstPrimaryUserID: null,
        filterByHashtags: [],
        filterByIsFollowing: false,
        filterByIsPinned: false,
        filterByIsPrimary: false,
        filterByPhaseID: null,
        filterByPhaseNames: [],
        filterByRoles: [],
        includeAggregateCounts: false,
        includeArchived: false,
        skip: offset || 0,
        sort: 'ProjectType',
        take: limit || 100
      };
      
      console.log('[Web API] Request body:', JSON.stringify(requestBody));
      
      const response = await this.request('POST', '/projectHub/search', {
        data: requestBody
      });

      console.log('[Web API] Projects response type:', typeof response);
      console.log('[Web API] Projects response keys:', response ? Object.keys(response) : 'null');
      console.log('[Web API] Projects response preview:', JSON.stringify(response).substring(0, 500));

      // Transform response to match expected format
      // Filevine web API returns: { success: true, data: { hits: [...], total: ... } }
      if (response) {
        // Check if response has success and data
        if (response.success && response.data) {
          // Projects are in data.hits!
          if (response.data.hits && Array.isArray(response.data.hits)) {
            console.log('[Web API] Found data.hits:', response.data.hits.length, 'total:', response.data.total);
            return {
              items: response.data.hits,
              total: response.data.total || response.data.hits.length
            };
          }
          
          // Fallback: data might be an array
          if (Array.isArray(response.data)) {
            console.log('[Web API] Found data array:', response.data.length);
            return {
              items: response.data,
              total: response.data.length
            };
          } else if (response.data.items && Array.isArray(response.data.items)) {
            console.log('[Web API] Found data.items:', response.data.items.length);
            return {
              items: response.data.items,
              total: response.data.total || response.data.items.length
            };
          } else if (response.data.projects && Array.isArray(response.data.projects)) {
            console.log('[Web API] Found data.projects:', response.data.projects.length);
            return {
              items: response.data.projects,
              total: response.data.total || response.data.projects.length
            };
          }
        }
        
        // Check if response has items directly
        if (response.items && Array.isArray(response.items)) {
          console.log('[Web API] Found items directly:', response.items.length);
          return {
            items: response.items,
            total: response.total || response.items.length
          };
        }
        
        // Check if response is an array directly
        if (Array.isArray(response)) {
          console.log('[Web API] Response is array:', response.length);
          return {
            items: response,
            total: response.length
          };
        }
      }
      
      console.warn('[Web API] Unexpected response format:', response);
      return { items: [], total: 0 };
    } catch (error) {
      console.error('[Web API] Error getting projects:', error);
      throw error;
    }
  }

  /**
   * Get single project
   */
  async getProject(projectId) {
    return this.request('GET', `/projects/${projectId}`);
  }

  /**
   * Get documents for a project
   * Filevine uses POST /api/projects/{projectId}/Docs with a request body
   *
   * @param {number} projectId - Project ID
   * @param {Object} options - Request options
   * @param {number} options.limit - Max number of documents to return (default: 100)
   * @param {number} options.offset - Offset for pagination (default: 0)
   * @param {number|null} options.folderID - Specific folder ID to filter by (default: null)
   * @param {boolean} options.allDocuments - If true, fetch ALL documents without folder filtering (default: false)
   */
  async getProjectDocuments(projectId, { limit = 100, offset = 0, folderID = null, allDocuments = false } = {}) {
    console.log('[Web API] Getting documents for project:', projectId, 'allDocuments:', allDocuments);

    try {
      let targetFolderID = folderID;

      // Only try to get root folder ID if:
      // 1. A specific folderID was NOT provided
      // 2. AND allDocuments is false (we want to filter by root folder)
      if (!targetFolderID && !allDocuments) {
        console.log('[Web API] No folderID provided and allDocuments=false, fetching root folder ID...');

        // Try to get folder tree to find root folder
        try {
          const folderTreeResponse = await this.request('POST', `/projects/${projectId}/limitedProjectFolderTree`, {
            data: {
              descendantFolderIDs: [],
              maxChildrenPerFolder: 500
            }
          });

          console.log('[Web API] Folder tree response:', JSON.stringify(folderTreeResponse).substring(0, 500));

          // Extract root folder ID from folder tree
          // Response structure: { success: true, data: { index: { projectRootFolderID: 1056487655, ... } } }
          if (folderTreeResponse.success && folderTreeResponse.data) {
            console.log('[Web API] Checking folder tree data structure...');
            console.log('[Web API] data.index exists:', !!folderTreeResponse.data.index);

            if (folderTreeResponse.data.index) {
              console.log('[Web API] index keys:', Object.keys(folderTreeResponse.data.index));
              if (folderTreeResponse.data.index.projectRootFolderID) {
                targetFolderID = folderTreeResponse.data.index.projectRootFolderID;
                console.log('[Web API] ✅ Found root folder ID from index:', targetFolderID);
              }
            }

            // Fallback checks
            if (!targetFolderID && folderTreeResponse.data.projectRootFolderID) {
              targetFolderID = folderTreeResponse.data.projectRootFolderID;
              console.log('[Web API] ✅ Found root folder ID from data:', targetFolderID);
            }
            if (!targetFolderID && folderTreeResponse.data.rootFolderID) {
              targetFolderID = folderTreeResponse.data.rootFolderID;
              console.log('[Web API] ✅ Found root folder ID:', targetFolderID);
            }

            if (!targetFolderID) {
              console.warn('[Web API] ⚠️ Could not find root folder ID in response');
              console.warn('[Web API] Full data structure:', JSON.stringify(folderTreeResponse.data).substring(0, 1000));
            }
          }
        } catch (error) {
          console.log('[Web API] Could not get folder tree, will try without folderID:', error.message);
        }
      }

      // Build request body
      // When allDocuments=true or targetFolderID is still null, omit folderID to get ALL documents
      const requestBody = {
        offset: offset || 0,
        filter: '',
        sort: 'uploaddate',
        filterSingleDocID: null,
        isAscending: false,
        skipDocIDs: [] // Array of doc IDs to skip (for pagination)
      };

      // Only add folderID if we have one and allDocuments is false
      if (targetFolderID && !allDocuments) {
        requestBody.folderID = targetFolderID;
        console.log('[Web API] Filtering by folderID:', targetFolderID);
      } else {
        console.log('[Web API] No folder filter - fetching ALL documents from project');
      }

      console.log('[Web API] Documents request body:', JSON.stringify(requestBody));
      
      // The correct endpoint is /docs/project/{projectId} (not /projects/{id}/Docs)
      const response = await this.request('POST', `/docs/project/${projectId}`, {
        data: requestBody
      });
      
      console.log('[Web API] Documents response:', JSON.stringify(response).substring(0, 500));
      
      // Handle response format
      if (response) {
        // Check if response has success and data
        if (response.success && response.data) {
          // Documents might be in data.items, data.docs, data.documents, or data directly
          if (Array.isArray(response.data)) {
            console.log('[Web API] Found data array:', response.data.length);
            return {
              items: response.data,
              total: response.data.length
            };
          } else if (response.data.items && Array.isArray(response.data.items)) {
            console.log('[Web API] Found data.items:', response.data.items.length);
            return {
              items: response.data.items,
              total: response.data.total || response.data.items.length
            };
          } else if (response.data.docs && Array.isArray(response.data.docs)) {
            console.log('[Web API] Found data.docs:', response.data.docs.length);
            return {
              items: response.data.docs,
              total: response.data.total || response.data.docs.length
            };
          } else if (response.data.documents && Array.isArray(response.data.documents)) {
            console.log('[Web API] Found data.documents:', response.data.documents.length);
            return {
              items: response.data.documents,
              total: response.data.total || response.data.documents.length
            };
          } else if (response.data.hits && Array.isArray(response.data.hits)) {
            console.log('[Web API] Found data.hits:', response.data.hits.length);
            return {
              items: response.data.hits,
              total: response.data.total || response.data.hits.length
            };
          }
        }
        
        // Check if response is an array directly
        if (Array.isArray(response)) {
          console.log('[Web API] Response is array:', response.length);
          return {
            items: response,
            total: response.length
          };
        }
        
        // Check if response has items directly
        if (response.items && Array.isArray(response.items)) {
          console.log('[Web API] Found items directly:', response.items.length);
          return {
            items: response.items,
            total: response.total || response.items.length
          };
        }
      }
      
      console.warn('[Web API] Unexpected documents response format:', response);
      return { items: [], total: 0 };
    } catch (error) {
      console.error('[Web API] Error getting documents:', error);
      throw error;
    }
  }

  /**
   * Get folder tree structure for a project
   * Returns folders organized in a tree structure
   */
  async getProjectFolderTree(projectId, { descendantFolderIDs = [], maxChildrenPerFolder = 500 } = {}) {
    console.log('[Web API] Getting folder tree for project:', projectId);
    
    try {
      const response = await this.request('POST', `/projects/${projectId}/limitedProjectFolderTree`, {
        data: {
          descendantFolderIDs,
          maxChildrenPerFolder
        }
      });
      
      console.log('[Web API] Folder tree response:', JSON.stringify(response).substring(0, 500));
      
      if (response.success && response.data) {
        // Extract folder structure from response
        // Response structure: 
        // {
        //   success: true,
        //   data: {
        //     index: {
        //       projectRootFolderID: 1056487655,
        //       childrenLookup: {
        //         "1056487655": [1056487657, 1056891576, ...], // child folder IDs
        //         "1056487657": [...], // nested children
        //       },
        //       folderLookup: { // folder details by ID
        //         "1056487655": { folderID: 1056487655, folderName: "...", ... },
        //         "1056487657": { folderID: 1056487657, folderName: "...", ... },
        //       }
        //     }
        //   }
        // }
        // Extract folder names from folderTree if available (when descendantFolderIDs is provided)
        const folderNameMap = {}; // Map of folderID -> folderName
        if (response.data.folderTree) {
          // Recursively extract folder names from the folderTree structure
          const extractFolderNames = (folder) => {
            if (folder && folder.id && folder.name) {
              folderNameMap[folder.id] = folder.name;
              folderNameMap[String(folder.id)] = folder.name;
            }
            if (folder.children && Array.isArray(folder.children)) {
              folder.children.forEach(child => extractFolderNames(child));
            }
          };
          extractFolderNames(response.data.folderTree);
          console.log('[Web API] Extracted folder names from folderTree:', Object.keys(folderNameMap).length, 'folders');
        }
        
        const folderTree = {
          rootFolderID: response.data.index?.projectRootFolderID,
          childrenLookup: response.data.index?.childrenLookup || {}, // Map of parentID -> [childIDs]
          folderLookup: response.data.index?.folderLookup || {}, // Map of folderID -> folder details
          folderNameMap: folderNameMap, // Map of folderID -> folderName (from folderTree)
          folderTree: response.data.folderTree || null, // Full folder tree structure
          index: response.data.index || {}
        };
        
        console.log('[Web API] ✅ Parsed folder tree');
        console.log('[Web API] Root folder ID:', folderTree.rootFolderID);
        console.log('[Web API] Children lookup keys:', Object.keys(folderTree.childrenLookup).slice(0, 10), '... (showing first 10)');
        console.log('[Web API] Folder lookup keys:', Object.keys(folderTree.folderLookup).slice(0, 10), '... (showing first 10)');
        console.log('[Web API] Folder name map keys:', Object.keys(folderTree.folderNameMap).slice(0, 10), '... (showing first 10)');
        
        // Log sample folder structure for debugging
        if (Object.keys(folderTree.folderLookup).length > 0) {
          const firstKey = Object.keys(folderTree.folderLookup)[0];
          const firstFolder = folderTree.folderLookup[firstKey];
          console.log('[Web API] Sample folder (key:', firstKey, '):', firstFolder);
          console.log('[Web API] Sample folder properties:', Object.keys(firstFolder || {}));
        }
        
        return folderTree;
      }
      
      throw new Error('Invalid folder tree response format');
    } catch (error) {
      console.error('[Web API] Error getting folder tree:', error);
      throw error;
    }
  }

  /**
   * Get download URL for a document
   * Returns a presigned S3 URL or direct download URL
   * 
   * The correct endpoint is: GET /api/docs/download/{docId}
   * This endpoint may return:
   * 1. JSON with a download URL
   * 2. The file directly (binary)
   * 3. A redirect to the file
   * 
   * We return the endpoint URL itself, which the download manager can fetch directly.
   */
  async getDocumentDownloadUrl(docId, projectId = null) {
    console.log('[Web API] Getting download URL for document:', docId);

    try {
      // The correct endpoint is /docs/download/{docId}
      const endpoint = `/docs/download/${docId}`;
      const baseUrl = 'https://product.filevineapp.com/api';
      const fullUrl = `${baseUrl}${endpoint}`;

      console.log('[Web API] Using download endpoint:', fullUrl);

      // Try to get JSON response first (in case it returns a URL)
      try {
        const response = await this.request('GET', endpoint);

        // Check if response has download URL
        if (response && typeof response === 'object') {
          if (response.url || response.downloadUrl) {
            const downloadUrl = response.url || response.downloadUrl;
            console.log('[Web API] ✅ Found download URL in response:', downloadUrl.substring(0, 100) + '...');
            return { url: downloadUrl, downloadUrl };
          }

          if (response.data?.url || response.data?.downloadUrl) {
            const downloadUrl = response.data.url || response.data.downloadUrl;
            console.log('[Web API] ✅ Found download URL in response.data:', downloadUrl.substring(0, 100) + '...');
            return { url: downloadUrl, downloadUrl };
          }
        }

        // If response is a string URL
        if (typeof response === 'string' && response.startsWith('http')) {
          console.log('[Web API] ✅ Response is direct URL');
          return { url: response, downloadUrl: response };
        }
      } catch (jsonError) {
        // If JSON parsing fails, the endpoint likely returns the file directly
        console.log('[Web API] JSON parse failed (likely binary response), using endpoint URL directly');
      }

      // If we get here, the endpoint returns the file directly
      // Return the endpoint URL so download manager can fetch it
      console.log('[Web API] Using endpoint URL directly (endpoint returns file):', fullUrl);
      return { url: fullUrl, downloadUrl: fullUrl };

    } catch (error) {
      console.error('[Web API] Error getting download URL:', error);
      throw error;
    }
  }

  /**
   * Download a document from Filevine
   * Returns ArrayBuffer of the file content
   *
   * @param {number} docId - Document ID
   * @param {number} projectId - Project ID (optional, not currently used but kept for consistency)
   * @returns {Promise<ArrayBuffer>} File content as ArrayBuffer
   */
  async downloadDocument(docId, projectId = null) {
    console.log('[Web API] Downloading document:', docId);

    if (!this.isAuthenticated()) {
      throw new Error('Not authenticated');
    }

    try {
      // Use the download endpoint directly
      const endpoint = `/docs/download/${docId}`;
      const baseUrl = 'https://product.filevineapp.com/api';
      const url = `${baseUrl}${endpoint}`;

      console.log('[Web API] Fetching document from:', url);

      // Make request with cookies
      const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Cookie': this.cookie,
          'Accept': '*/*'
        },
        credentials: 'include'
      });

      if (!response.ok) {
        const errorText = await response.text();
        console.error('[Web API] Download error:', response.status, errorText.substring(0, 200));
        throw new Error(`Download failed (${response.status}): ${errorText.substring(0, 200)}`);
      }

      // Get the file content as ArrayBuffer
      const arrayBuffer = await response.arrayBuffer();
      console.log('[Web API] ✅ Document downloaded, size:', arrayBuffer.byteLength, 'bytes');

      return arrayBuffer;

    } catch (error) {
      console.error('[Web API] Error downloading document:', error);
      throw error;
    }
  }

  /**
   * Create a custom field in Filevine
   *
   * @param {number} orgId - Organization ID
   * @param {number} sectionId - Custom Section ID
   * @param {Object} fieldData - Field configuration
   * @returns {Promise<Object>} Created field data
   */
  async createCustomField(orgId, sectionId, fieldData) {
    console.log('[Web API] Creating custom field:', { orgId, sectionId, fieldData });

    if (!this.isAuthenticated()) {
      throw new Error('Not authenticated');
    }

    try {
      const endpoint = `/advanced/orgs/${orgId}/customSections/${sectionId}/customFields/create`;
      const baseUrl = 'https://product.filevineapp.com/api';
      const url = `${baseUrl}${endpoint}`;

      console.log('[Web API] Posting to:', url);

      // Build the payload based on Filevine's expected format
      const payload = {
        id: null,
        name: fieldData.name,
        isObsolete: false,
        isRequired: fieldData.isRequired || false,
        isLocked: false,
        fieldSelector: fieldData.fieldSelector,
        customFieldType: parseInt(fieldData.customFieldType),
        row: parseInt(fieldData.row),
        orderInRow: fieldData.orderInRow || 0,
        notes: fieldData.notes || '',
        instructions: fieldData.instructions || null,
        customActionButtonType: fieldData.customActionButtonType || null,
        personFilter: fieldData.personFilter || 0,
        dropdownItems: fieldData.dropdownItems || [],
        docGenTemplate: fieldData.docGenTemplate || null,
        docGenPdfGuideTemplate: fieldData.docGenPdfGuideTemplate || null,
        customFieldDetails: this.getCustomFieldDetails(fieldData),
        behavior: fieldData.behavior || 0,
        behaviors: fieldData.behaviors || [],
        availablePdfFields: fieldData.availablePdfFields || null,
        pdfDocGenFields: fieldData.pdfDocGenFields || null,
        quickbooksAmountFieldID: fieldData.quickbooksAmountFieldID || null,
        quickbooksDescriptionFieldID: fieldData.quickbooksDescriptionFieldID || null,
        parameterCustomFieldID: fieldData.parameterCustomFieldID || 0,
        whenParameter: fieldData.whenParameter || '',
        whenTestType: fieldData.whenTestType || null,
        fieldWidgetOption: fieldData.fieldWidgetOption || 'Field',
        isBtnTitleSameAsName: fieldData.isBtnTitleSameAsName || false,
        originalFieldSelector: fieldData.originalFieldSelector || null,
        uploads: fieldData.uploads || []
      };

      // Add allowedBehaviorParameterFields if needed
      if (fieldData.allowedBehaviorParameterFields) {
        payload.allowedBehaviorParameterFields = fieldData.allowedBehaviorParameterFields;
      }

      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Cookie': this.cookie,
          'Accept': 'application/json, text/plain, */*'
        },
        credentials: 'include',
        body: JSON.stringify(payload)
      });

      if (!response.ok) {
        const errorText = await response.text();
        console.error('[Web API] Create field error:', response.status, errorText);
        throw new Error(`Failed to create custom field (${response.status}): ${errorText.substring(0, 200)}`);
      }

      const result = await response.json();
      console.log('[Web API] ✅ Custom field created:', result);

      return result;

    } catch (error) {
      console.error('[Web API] Error creating custom field:', error);
      throw error;
    }
  }

  /**
   * Get custom field details based on field type
   * Different field types require different customFieldDetails structure
   *
   * @param {Object} fieldData - Field configuration
   * @returns {Object} customFieldDetails object
   */
  getCustomFieldDetails(fieldData) {
    // If customFieldDetails is explicitly provided, use it
    if (fieldData.customFieldDetails) {
      return fieldData.customFieldDetails;
    }

    const fieldType = parseInt(fieldData.customFieldType);

    // Dropdown List (9) and Multi-Select List (26) need multipleChoiceDisplayType
    if (fieldType === 9 || fieldType === 26) {
      return {
        icon: null,
        multipleChoiceDisplayType: 0
      };
    }

    // Text fields (6, 17) might have analysis features
    if (fieldType === 6 || fieldType === 17) {
      return {
        isAnalysisOutput: false,
        analyzeCustomFieldID: 0,
        analysisTypeID: 0,
        analyzeCustomFieldName: ''
      };
    }

    // Default for most field types
    return {
      icon: null
    };
  }

  /**
   * Get custom project type schema (includes all custom sections)
   *
   * @param {number} orgId - Organization ID
   * @param {number} projectTypeId - Project Type ID
   * @returns {Promise<Object>} Project type schema with custom sections
   */
  async getCustomProjectType(orgId, projectTypeId) {
    console.log('[Web API] Getting custom project type:', { orgId, projectTypeId });

    if (!this.isAuthenticated()) {
      throw new Error('Not authenticated');
    }

    try {
      const endpoint = `/advanced/orgs/${orgId}/customProjectTypes/${projectTypeId}`;
      const baseUrl = 'https://product.filevineapp.com/api';
      const url = `${baseUrl}${endpoint}`;

      console.log('[Web API] Fetching from:', url);

      const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Cookie': this.cookie,
          'Accept': 'application/json'
        },
        credentials: 'include'
      });

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

      const result = await response.json();
      console.log('[Web API] ✅ Project type retrieved, sections count:', result.data?.customSections?.length);

      return result;

    } catch (error) {
      console.error('[Web API] Error getting project type:', error);
      throw error;
    }
  }
}

// Singleton instance
export const webApiClient = new WebApiClient();
