import { DrupalJsonApiParams } from 'drupal-jsonapi-params';
import { getResourcePath } from './resourcePaths';

/**
 * Builds a JSON:API URL with the given parameters
 */
export const buildJsonApiUrl = async ({
  selectedEntity,
  selectedBundle,
  selectedFields,
  includes,
  filters,
  sorts,
  page,
  limit,
  relatedEntityFields,
  baseUrl,
  showNotification
}) => {
  if (!selectedEntity || !selectedBundle) {
    return '';
  }

  // Initialize the JSON:API parameters builder
  const apiParams = new DrupalJsonApiParams();

  // Add entity type for fields
  const entityType = `${selectedEntity}--${selectedBundle}`;

  // Ensure relationship fields are automatically included in selected fields
  // This makes sure that when a relationship is included, its field is also selected
  let effectiveSelectedFields = [...selectedFields];

  // For each include, ensure its relationship field is added to the selected fields
  includes.forEach(include => {
    if (!effectiveSelectedFields.includes(include)) {
      effectiveSelectedFields.push(include);
      console.log(`Auto-adding relationship field "${include}" to selected fields for URL building`);
    }
  });

  // Fields - Process them by entity type
  if (effectiveSelectedFields.length > 0) {
    // Group fields by their entity types
    const fieldsByEntityType = {};

    // Initialize with the main entity type
    fieldsByEntityType[entityType] = [];

    // Process each field
    effectiveSelectedFields.forEach(field => {
      // Check if it's a relationship field (contains a dot)
      if (field.includes('.')) {
        // Split into relationship name and field name
        const [relationshipName, fieldName] = field.split('.');

        // First, check if this relationship is actually in the includes list
        // If not, don't even try to process it - it's probably a field that's about to be removed
        if (!includes.includes(relationshipName)) {
          // Silently skip it - this field is not included
          return;
        }

        // Find target entity type for this relationship
        if (relationshipName && fieldName && relatedEntityFields[relationshipName]) {
          // Extract the first field's full metadata to get target type information
          const sampleField = relatedEntityFields[relationshipName][0];
          if (sampleField && sampleField.fieldDef) {
            // Extract field definition which should have type information

            // Use relationship info to determine the entity type and bundle
            // Try to extract from the loaded relationship schema
            let targetType = Object.keys(apiParams.data.fields).find(type =>
              type.startsWith(relationshipName + '--')
            );

            // If not found in already defined fields, check if we have the info from schema loading
            if (!targetType) {
              // Use a method to find the target entity type based on the relationship name
              // This requires the relatedEntityFields to contain target type information
              const targetEntityInfo = findTargetEntityTypeFromRelationship(relationshipName, includes, relatedEntityFields);
              if (targetEntityInfo) {
                targetType = targetEntityInfo;
              } else {
                // Only log a warning if the relationship is actually included
                if (includes.includes(relationshipName)) {
                  console.warn(`Could not determine target entity type for relationship: ${relationshipName}`);
                  // Only show notification if the page is fully loaded (not during initial page load)
                  if (document.readyState === "complete" && showNotification) {
                    // Show a notification to the user
                    showNotification(`Could not determine entity type for relationship field "${field}". Field selection will be ignored in the API request.`, 'warning');
                  }
                }
                // Don't add the field to any entity type as it would be formatted incorrectly
                return;
              }
            }

            // Initialize if this entity type hasn't been seen before
            if (!fieldsByEntityType[targetType]) {
              fieldsByEntityType[targetType] = [];
            }

            // Add the field without the relationship prefix to the target entity type
            fieldsByEntityType[targetType].push(fieldName);
          } else {
            // Cannot determine target type for this relationship field
            // Only log and show a warning if the relationship is actually included
            if (includes.includes(relationshipName)) {
              console.warn(`Missing field definitions for relationship: ${relationshipName}`);
              // Only show notification if the page is fully loaded (not during initial page load)
              if (document.readyState === "complete" && showNotification) {
                // Show a notification to the user
                showNotification(`Could not determine entity type for relationship field "${field}". Field selection will be ignored in the API request.`, 'warning');
              }
            }
            // Don't add the field to any entity type as it would be formatted incorrectly
            return;
          }
        } else {
          // Cannot find relationship info for this field
          // If relationship is included but we don't have data, this is a legitimate problem
          if (includes.includes(relationshipName)) {
            console.warn(`No relationship data found for: ${relationshipName}`);

            // Don't show warnings if we're during initial loading
            if (document.readyState === "complete" && showNotification) {
              // Only show notification if page is fully loaded
              showNotification(`Could not find relationship data for field "${field}". The relationship is included but data is not available.`, 'warning');
            }

            // Skip this field but avoid notification since the include exists
            return;
          }

          // Otherwise, just skip silently - the field is no longer included
          return;
        }
      } else {
        // Regular field - add to main entity
        fieldsByEntityType[entityType].push(field);
      }
    });

    // Add fields by entity type to the parameters
    Object.entries(fieldsByEntityType).forEach(([type, fields]) => {
      if (fields.length > 0) {
        apiParams.addFields(type, fields);
      }
    });
  }

  // Includes
  if (includes.length > 0) {
    apiParams.addInclude(includes);
  }

  // Add filter groups first (must be created before adding filters to them)
  if (filters.length > 0) {
    // Create a mapping of our group IDs to the names used in the library
    const groupMap = {};

    // First pass: create all groups
    filters.filter(filter => filter.group).forEach(group => {
      // Use the group name as the identifier or fallback to the groupId if no name was provided
      const groupId = group.groupId;
      const groupName = group.groupName || `Group ${groupId}`;

      // Store in our mapping for later use
      groupMap[groupId] = groupName;

      // Add the group to the parameters
      apiParams.addGroup(groupName, group.conjunction);
    });

    // Second pass: add all filters
    filters.forEach(filter => {
      if (filter.group) {
        // Process group members
        if (filter.members && filter.members.length > 0) {
          const groupName = groupMap[filter.groupId];

          filter.members.forEach(memberFilter => {
            // Convert our operator 'value' to '=' for the library
            const operator = memberFilter.operator === 'value' ? '=' : memberFilter.operator;

            // Handle special cases for operators that expect array values
            if (['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'].includes(operator) && typeof memberFilter.value === 'string') {
              if (memberFilter.value.includes(',')) {
                // Split the comma-separated string into an array
                const values = memberFilter.value.split(',').map(item => item.trim());

                // Add the filter with the array value to the specified group
                apiParams.addFilter(memberFilter.field, values, operator, groupName);
              } else {
                // If no comma, use as single value
                apiParams.addFilter(memberFilter.field, memberFilter.value, operator, groupName);
              }
            } else {
              // Standard filters go here
              apiParams.addFilter(memberFilter.field, memberFilter.value, operator, groupName);
            }
          });
        }
      } else {
        // Handle standalone filters (not in groups)
        const operator = filter.operator === 'value' ? '=' : filter.operator;

        // Handle special cases for operators that expect array values
        if (['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'].includes(operator) && typeof filter.value === 'string') {
          if (filter.value.includes(',')) {
            // Split the comma-separated string into an array
            const values = filter.value.split(',').map(item => item.trim());

            // Add the filter with the array value
            apiParams.addFilter(filter.field, values, operator);
          } else {
            // If no comma, use as single value
            apiParams.addFilter(filter.field, filter.value, operator);
          }
        } else {
          // Standard filters go here
          apiParams.addFilter(filter.field, filter.value, operator);
        }
      }
    });
  }

  // Sorts
  if (sorts.length > 0) {
    sorts.forEach(sort => {
      apiParams.addSort(sort.field, sort.direction.toUpperCase());
    });
  }

  // Pagination
  if (limit !== 10) {
    apiParams.addPageLimit(limit);
  }

  if (page > 0) {
    apiParams.addPageOffset(page * limit);
  }

  // Build the URL with the query parameters
  const resourcePath = await getResourcePath(baseUrl, entityType)
  const queryString = apiParams.getQueryString();

  return `${resourcePath}${queryString ? '?' + queryString : ''}`;
};

/**
 * Helper function to find target entity type from relationship name
 */
const findTargetEntityTypeFromRelationship = (relationshipName, includes, relatedEntityFields) => {
  // Only proceed if this relationship is included
  if (includes.includes(relationshipName)) {
    // If we have related entity fields data, extract the type information
    if (relatedEntityFields[relationshipName] && relatedEntityFields[relationshipName].length > 0) {
      // First try to find the base relationship field as it should have the target entity type
      const baseRelationshipField = relatedEntityFields[relationshipName].find(
        field => field.isBaseRelationship === true
      );

      if (baseRelationshipField && baseRelationshipField.targetEntityType) {
        return baseRelationshipField.targetEntityType;
      }

      // If not found in base relationship, try any field from this relationship
      for (const field of relatedEntityFields[relationshipName]) {
        if (field.targetEntityType) {
          return field.targetEntityType;
        }
      }
    }

    // Only hardcode uid as it's a core field with a consistent type
    if (relationshipName === 'uid') {
      return 'user--user';
    }
  }

  // Could not determine target type
  return null;
};
