import moment from "moment";
import {
  convertToOptions,
  DATE_FILTER_OPTIONS,
  NON_TEXT_FILTER_OPTIONS,
  TEXT_FILTER_OPTIONS,
} from "../../utils/helper";

// Helper: Determine operator list based on column definition
export const getOperatorListForColumn = (columnDef) => {
  // Priority 1: Custom operator list
  if (columnDef?.operationFilterOptions) {
    return columnDef.operationFilterOptions;
  }

  // Priority 2: Date range picker
  if (columnDef?.thirdOptionAsDateRangePicker) {
    return [{ label: "In range", value: "inRange" }];
  }

  // Priority 3: Check filter type
  switch (columnDef?.filter) {
    case "agDateColumnFilter":
      return DATE_FILTER_OPTIONS;

    case "agNumberColumnFilter":
      return NON_TEXT_FILTER_OPTIONS;

    case "agTextColumnFilter":
      // Boolean columns with text filter
      if (columnDef?.dataType === "boolean") {
        return [
          { label: "Equals", value: "equals" },
          { label: "Not equal", value: "notEqual" },
        ];
      }
      // Number type with text filter (formatted numbers)
      if (columnDef?.type === "number") {
        return TEXT_FILTER_OPTIONS;
      }
      return TEXT_FILTER_OPTIONS;

    default:
      break;
  }

  // Priority 4: Check dataType
  if (columnDef?.dataType === "boolean") {
    return [
      { label: "Equals", value: "equals" },
      { label: "Not equal", value: "notEqual" },
    ];
  }

  // Priority 5: Check type (number without explicit filter)
  if (columnDef?.type === "number") {
    return NON_TEXT_FILTER_OPTIONS;
  }

  // Default: Text operators
  return TEXT_FILTER_OPTIONS;
};

// Helper: Get default operation for a column
export const getDefaultOperationForColumn = (columnDef, filter = null) => {
  // Find "Equals" option if available
  const operatorList = getOperatorListForColumn(columnDef);
  const equalsOption = operatorList.find((opt) => opt.value === "equals");

  // Date range picker always uses "inRange"
  if (columnDef?.thirdOptionAsDateRangePicker) {
    return { label: "In range", value: "inRange" };
  }

  // Columns that prefer "Equals" as default
  const prefersEquals =
    columnDef?.filter === "agDateColumnFilter" ||
    filter?.filterType === "date" ||
    columnDef?.filter === "agNumberColumnFilter" ||
    columnDef?.dataType === "boolean" ||
    filter?.filterType === "boolean" ||
    (columnDef?.type === "number" &&
      columnDef?.filter === "agNumberColumnFilter");

  if (prefersEquals && equalsOption) {
    return equalsOption;
  }

  // Default to first operator in list
  return operatorList[0];
};

/**
 * Removes special characters from numeric strings
 * Supports: currency symbols ($, €, £, ¥, ₹), commas, spaces, percentages
 * @param {string} value - The value to clean
 * @returns {string} - Cleaned value
 */
export const cleanNumericValue = (value) => {
  if (typeof value !== "string") {
    return value;
  }
  // Remove currency symbols, commas, spaces, and percentage signs
  return value.replace(/[$€£¥₹,\s%]/g, "");
};

/**
 * Parses a filter value based on the column type and definition
 * @param {*} value - The raw filter value
 * @param {object} columnDef - The column definition from AG Grid
 * @returns {object} - { parsedValue, filterType }
 */
export const parseFilterValue = (value, columnDef) => {
  const type = columnDef?.type;
  const filter = columnDef?.filter;
  let parsedValue;
  let filterType = "contains";

  // Handle date range picker values (check this first before single date)
  if (
    value &&
    typeof value === "object" &&
    value?.startDate !== undefined &&
    value?.endDate !== undefined
  ) {
    const startDate = moment.isMoment(value.startDate)
      ? value.startDate
      : moment(value.startDate);
    const endDate = moment.isMoment(value.endDate)
      ? value.endDate
      : moment(value.endDate);

    parsedValue = {
      startDate: startDate.isValid() ? startDate.format("YYYY-MM-DD") : null,
      endDate: endDate.isValid() ? endDate.format("YYYY-MM-DD") : null,
    };
    return { parsedValue, filterType };
  }

  // Handle date columns and single date picker (moment object)
  if (filter === "agDateColumnFilter") {
    filterType = "equals";

    // Handle moment objects
    if (value && typeof value === "object" && moment.isMoment(value)) {
      parsedValue = value.format("YYYY-MM-DD");
      return { parsedValue, filterType };
    }

    // Handle date strings
    if (value && typeof value === "string") {
      const dateMoment = moment(
        value,
        ["MM-DD-YYYY", "YYYY-MM-DD", "MM/DD/YYYY"],
        true
      );
      if (dateMoment.isValid()) {
        parsedValue = dateMoment.format("YYYY-MM-DD");
        return { parsedValue, filterType };
      }
    }

    // Handle objects with format method
    if (
      value &&
      typeof value === "object" &&
      typeof value?.format === "function"
    ) {
      parsedValue = value.format("YYYY-MM-DD");
      return { parsedValue, filterType };
    }

    parsedValue = undefined;
    return { parsedValue, filterType };
  }

  // Handle number columns with special characters
  if (filter === "agNumberColumnFilter" || type === "number") {
    const cleanedValue = cleanNumericValue(value);
    const numericValue = parseFloat(cleanedValue);

    // Return null for invalid numbers
    if (isNaN(numericValue) || cleanedValue === "") {
      return { parsedValue: null, filterType };
    }

    parsedValue = numericValue;
    filterType = "equals";
    return { parsedValue, filterType };
  }

  // Handle text columns and other types
  parsedValue =
    typeof value === "string"
      ? value
      : typeof value === "object" && value?.value
      ? value.value
      : value;

  return { parsedValue, filterType };
};

/**
 * Determines if a column value is a string or number by checking first row data
 * Used to decide between "equals" (numbers) vs "contains" (strings) filter type
 * @param {object} api - AG Grid API
 * @param {string} columnId - Column ID to check
 * @returns {boolean} - true if column contains string values
 */
export const isColumnDataString = (api, columnId) => {
  let firstValue = null;
  api.forEachNode((node) => {
    if (firstValue === null && node.data) {
      firstValue = node.data[columnId];
    }
  });
  return typeof firstValue === "string";
};

/**
 * Builds a filter model entry for AG Grid
 * Supports all AG Grid filter operators including:
 * - Text: contains, notContains, equals, notEqual, startsWith, endsWith, blank, notBlank
 * - Number: equals, notEqual, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, blank, notBlank
 * - Date: equals, notEqual, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, inRange, blank, notBlank
 *
 * @param {*} parsedValue - The parsed filter value
 * @param {string} filterType - The filter type (equals, contains, etc.)
 * @param {object} columnDef - The column definition
 * @param {string} operationType - Optional operation type from advance search
 * @returns {object|undefined} - Filter model entry or undefined if invalid
 */
export const buildFilterModelEntry = (
  parsedValue,
  filterType,
  columnDef,
  operationType = null
) => {
  const type = columnDef?.type;
  const filter = columnDef?.filter;
  const operator = operationType || filterType;

  // Handle blank and notBlank operators (they don't need a value)
  if (operator === "blank" || operator === "notBlank") {
    // Determine the appropriate filterType based on column definition
    let filterTypeForBlank = "text";
    if (filter === "agDateColumnFilter") {
      filterTypeForBlank = "date";
    } else if (filter === "agNumberColumnFilter" || type === "number") {
      filterTypeForBlank = "number";
    } else if (
      columnDef?.dataType === "boolean" &&
      filter === "agTextColumnFilter"
    ) {
      // Boolean columns using text filter should use "text" filterType to match AG Grid's filter
      filterTypeForBlank = "text";
    }

    return {
      filterType: filterTypeForBlank,
      type: operator,
    };
  }

  // Check if value is valid for other operators
  if (parsedValue === null || parsedValue === undefined || parsedValue === "") {
    return undefined;
  }

  // Handle date range values (object with startDate and endDate)
  if (
    filter === "agDateColumnFilter" &&
    typeof parsedValue === "object" &&
    parsedValue.startDate &&
    parsedValue.endDate
  ) {
    return {
      filterType: "date",
      type: "inRange",
      dateFrom: parsedValue.startDate,
      dateTo: parsedValue.endDate,
    };
  }

  // Handle single date columns with all operators
  if (filter === "agDateColumnFilter") {
    return {
      filterType: "date",
      type: operator,
      dateFrom: parsedValue,
    };
  }

  // Handle number columns with agNumberColumnFilter
  // AG Grid number filter supports: equals, notEqual, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, blank, notBlank
  if (filter === "agNumberColumnFilter") {
    return {
      filterType: "number",
      type: operator,
      filter: parsedValue,
    };
  }

  // Handle boolean columns
  if (columnDef?.dataType === "boolean") {
    return {
      filterType: "text",
      type: operator,
      filter: String(parsedValue),
    };
  }

  // If type is number but filter is agTextColumnFilter (e.g., formatted numbers like "$11.38")
  // Use text filter format for text-based searching
  if (type === "number" && filter === "agTextColumnFilter") {
    return {
      filterType: "text",
      type: operator,
      filter: String(parsedValue),
    };
  }

  // If type is number with no explicit filter, use number filter
  if (type === "number") {
    return {
      filterType: "number",
      type: operator,
      filter: parsedValue,
    };
  }

  // Handle text columns with all operators
  // AG Grid text filter supports: contains, notContains, equals, notEqual, startsWith, endsWith, blank, notBlank
  const filterEntry = {
    type: operator,
    filter: parsedValue,
    filterType: "text",
  };

  return filterEntry;
};

/**
 * Recursively finds a column definition by field or colId
 * @param {array} colDefs - Array of column definitions
 * @param {string} field - Field or colId to find
 * @returns {object|null} - Column definition or null
 */
export const findColumnDef = (colDefs, field) => {
  for (const col of colDefs) {
    // Check children recursively
    if (col.children) {
      const found = findColumnDef(col.children, field);
      if (found) return found;
    }
    if (
      col.toShowDuplicateColumnInAdvanceSearch &&
      col.columnFieldsInAdvanceSearch
    ) {
      const index = col.columnFieldsInAdvanceSearch?.indexOf(field);
      if (index !== -1) {
        return {
          ...col,
          currentColumnField: `${field}${index + 1}`, // add index + 1
        };
      }
    } else if (
      (col.colId && col.colId === field) ||
      (col.field && col.field === field)
    ) {
      return col;
    }
  }
  return null;
};

/**
 * Applies inline search filter for a single column
 * Handles special logic for number columns with formatted vs raw data
 * @param {*} value - Search value
 * @param {object} columnDef - Column definition
 * @param {object} api - AG Grid API
 * @param {string} columnId - Column ID
 * @returns {object} - { parsedValue, filterType }
 */
export const parseInlineSearchValue = (value, columnDef, api, columnId) => {
  const type = columnDef?.type;
  const filter = columnDef?.filter;

  // Date columns
  if (filter === "agDateColumnFilter") {
    // Handle date range objects (from DateRangePicker)
    if (
      value &&
      typeof value === "object" &&
      value?.startDate !== undefined &&
      value?.endDate !== undefined
    ) {
      const startDate = moment.isMoment(value.startDate)
        ? value.startDate
        : moment(value.startDate);
      const endDate = moment.isMoment(value.endDate)
        ? value.endDate
        : moment(value.endDate);

      return {
        parsedValue: {
          startDate: startDate.isValid()
            ? startDate.format("YYYY-MM-DD")
            : null,
          endDate: endDate.isValid() ? endDate.format("YYYY-MM-DD") : null,
        },
        filterType: "inRange",
      };
    }
    // Handle single moment objects
    if (value && typeof value === "object" && moment.isMoment(value)) {
      return {
        parsedValue: value.format("YYYY-MM-DD"),
        filterType: "equals",
      };
    }
    // Handle date strings
    if (value && typeof value === "string") {
      const dateMoment = moment(value, [
        "MM-DD-YYYY",
        "YYYY-MM-DD",
        "MM/DD/YYYY",
      ]);
      if (dateMoment.isValid()) {
        return {
          parsedValue: dateMoment.format("YYYY-MM-DD"),
          filterType: "equals",
        };
      }
    }
    return { parsedValue: undefined, filterType: "equals" };
  }

  // Number columns with special character handling
  if (filter === "agNumberColumnFilter" || type === "number") {
    const cleanedValue = cleanNumericValue(value);
    const parsedNumber = parseFloat(cleanedValue);

    if (isNaN(parsedNumber) || cleanedValue === "") {
      return { parsedValue: null, filterType: "contains" };
    }

    // Check if column data is string or number to determine filter type
    const isStringColumn = isColumnDataString(api, columnId);

    if (isStringColumn) {
      // Column contains formatted strings like "$11.38" - use original value for contains match
      return { parsedValue: value, filterType: "contains" };
    } else {
      // Column contains raw numbers - use parsed number for exact match
      return { parsedValue: parsedNumber, filterType: "equals" };
    }
  }

  // Boolean columns - use equals for exact match
  if (columnDef?.dataType === "boolean") {
    return { parsedValue: value, filterType: "equals" };
  }

  // Text columns
  return { parsedValue: value, filterType: "contains" };
};

/**
 * Validates if a parsed value is valid for filtering
 * @param {*} parsedValue - The parsed value to validate
 * @returns {boolean} - true if valid
 */
export const isValidFilterValue = (parsedValue) => {
  if (parsedValue === null || parsedValue === undefined) {
    return false;
  }

  if (typeof parsedValue === "number" && isNaN(parsedValue)) {
    return false;
  }

  if (typeof parsedValue === "string" && parsedValue.trim() === "") {
    return false;
  }

  // Handle date range objects
  if (
    typeof parsedValue === "object" &&
    parsedValue.startDate !== undefined &&
    parsedValue.endDate !== undefined
  ) {
    // Valid if both dates are present and not null
    return parsedValue.startDate !== null && parsedValue.endDate !== null;
  }

  return true;
};

/**
 * Gets the display label for a column (handles nested/hierarchical columns)
 * @param {string} columnId - The column ID
 * @param {object} columnDef - The column definition
 * @param {array} colDefs - All column definitions
 * @returns {string} - Display label for the column
 */
export const getColumnLabel = (columnId, columnDef, colDefs) => {
  // Start with basic label from column definition
  let label = columnDef?.headerName || columnId;

  // Try to get hierarchical label from convertToOptions
  if (colDefs) {
    const columnOptions = convertToOptions(colDefs);
    const columnOption = columnOptions.find((opt) => opt.value === columnId);
    if (columnOption?.label) {
      label = columnOption.label;
    }
  }

  return label;
};

/**
 * Validates if a filter in the filter model has a valid value
 * @param {object} filter - Filter object from filter model
 * @returns {boolean} - true if filter has valid value
 */
export const hasValidFilterValue = (filter) => {
  if (!filter || filter === undefined || filter === null) {
    return false;
  }

  const hasValue =
    (filter.filter !== undefined &&
      filter.filter !== null &&
      filter.filter !== "") ||
    (filter.dateFrom !== undefined &&
      filter.dateFrom !== null &&
      filter.dateFrom !== "");

  return hasValue;
};

/**
 * Converts AG Grid filter model entry to advance search modal format
 * @param {string} columnId - Column ID
 * @param {object} filter - Filter from filter model
 * @param {object} columnDef - Column definition
 * @param {array} colDefs - All column definitions
 * @returns {object} - Filter in modal format
 */
export const convertFilterToModalFormat = (
  columnId,
  filter,
  columnDef,
  colDefs
) => {
  const columnLabel = getColumnLabel(columnId, columnDef, colDefs);

  // Get the filter value based on filter type
  let value = filter?.filter || "";

  if (filter?.dateFrom) {
    // Handle date strings (may have time component from AG Grid)
    if (typeof filter.dateFrom === "string") {
      value = filter.dateFrom.split(" ")?.[0];
    } else if (filter.dateFrom instanceof Date) {
      // Fallback for Date objects (shouldn't happen but handle it)
      value = moment(filter.dateFrom).format("YYYY-MM-DD");
    } else {
      value = filter.dateFrom;
    }
  }

  // Determine operation type and label
  let operationType = filter?.type;
  let operationLabel;

  if (!operationType) {
    // Use default operation based on column type
    const defaultOp = getDefaultOperationForColumn(columnDef, filter);
    operationType = defaultOp.value;
    operationLabel = defaultOp.label;
  } else {
    // Format the label from the operation type
    operationLabel =
      operationType.charAt(0).toUpperCase() + operationType.slice(1);
  }

  return {
    column: {
      label: columnLabel,
      value: columnId,
      type: filter?.filterType || "text",
    },
    operation: {
      label: operationLabel,
      value: operationType,
    },
    value: value,
  };
};

/**
 * Gets column definition from AG Grid API or colDefs
 * @param {object} api - AG Grid API
 * @param {string} colId - Column ID
 * @param {array} colDefs - All column definitions
 * @returns {object|null} - Column definition
 */
const getColumnDefById = (api, colId, colDefs) => {
  const column = api?.getColumn(colId);
  if (column) {
    return column.getColDef();
  }
  if (colDefs) {
    return findColumnDef(colDefs, colId);
  }
  return null;
};

/**
 * Converts all active filters from AG Grid to modal format
 * @param {object} filterModel - AG Grid filter model
 * @param {object} api - AG Grid API
 * @param {array} colDefs - All column definitions
 * @returns {array} - Array of filters in modal format
 */
const convertActiveFiltersToModalFormat = (filterModel, api, colDefs) => {
  return Object.keys(filterModel)
    .filter((colId) => hasValidFilterValue(filterModel[colId]))
    .map((colId) => {
      const filter = filterModel[colId];
      const colDef = getColumnDefById(api, colId, colDefs);
      return convertFilterToModalFormat(colId, filter, colDef, colDefs);
    });
};

/**
 * Prepares value for modal based on column type using strategy pattern
 * @param {*} inputValue - Raw input value
 * @param {object} columnDef - Column definition
 * @param {function} getOptionFromDropdownValue - Function to convert dropdown values
 * @returns {*} - Prepared value for modal
 */
const prepareValueForModal = (
  inputValue,
  columnDef,
  getOptionFromDropdownValue
) => {
  const columnType = columnDef?.type;

  // Strategy pattern for different column types
  switch (columnType) {
    case "dropdown":
    case "select":
      // Handle dropdown columns with options
      if (columnDef?.thirdOptionDropdownOptions && inputValue) {
        const optionObject = getOptionFromDropdownValue(inputValue, columnDef);
        return typeof optionObject === "object" &&
          optionObject !== null &&
          optionObject.label
          ? optionObject
          : inputValue;
      }
      return inputValue;

    case "date":
      // Date values are already formatted, just return
      return inputValue;

    case "number":
      // Number values are already parsed, just return
      return inputValue;

    case "text":
    default:
      // Text and other types - check if has dropdown options
      if (columnDef?.thirdOptionDropdownOptions && inputValue) {
        const optionObject = getOptionFromDropdownValue(inputValue, columnDef);
        return typeof optionObject === "object" &&
          optionObject !== null &&
          optionObject.label
          ? optionObject
          : inputValue;
      }
      return inputValue;
  }
};

/**
 * Creates a new filter entry for modal
 * @param {string} columnId - Column ID
 * @param {*} value - Filter value
 * @param {object} columnDef - Column definition
 * @param {array} colDefs - All column definitions
 * @returns {object} - Filter entry in modal format
 */
const createModalFilterEntry = (columnId, value, columnDef, colDefs) => {
  return {
    column: {
      label: getColumnLabel(columnId, columnDef, colDefs),
      value: columnId,
      type: columnDef?.type || "text",
    },
    operation: getDefaultOperationForColumn(columnDef),
    value: value,
  };
};

/**
 * Updates existing filters or adds new filter
 * @param {array} existingFilters - Current filters in modal format
 * @param {string} columnId - Column ID being updated
 * @param {*} value - New filter value
 * @param {object} columnDef - Column definition
 * @param {array} colDefs - All column definitions
 * @returns {array} - Updated filters array
 */
export const updateOrAddFilter = (
  existingFilters,
  columnId,
  value,
  columnDef,
  colDefs
) => {
  const isAlreadyPresent = existingFilters.find(
    (f) => f?.column?.value === columnId
  );

  if (isAlreadyPresent) {
    // Update existing filter
    return existingFilters.map((f) =>
      f?.column?.value === columnId ? { ...f, value } : f
    );
  } else {
    // Add new filter
    return [
      ...existingFilters,
      createModalFilterEntry(columnId, value, columnDef, colDefs),
    ];
  }
};

/**
 * Syncs AG Grid filter model with advance search modal state
 * Clean, step-by-step approach for better maintainability
 *
 * @param {object} api - AG Grid API
 * @param {string} currentColumnId - Current column being filtered
 * @param {*} inputValue - The filter value
 * @param {object} columnDef - Column definition
 * @param {array} colDefs - All column definitions
 * @param {function} setValue - Form setValue function
 * @param {function} setInitialFilters - Set initial filters function
 * @param {function} getOptionFromDropdownValue - Function to convert dropdown values
 */
export const syncFiltersWithModal = (
  api,
  currentColumnId,
  inputValue,
  columnDef,
  colDefs,
  setValue,
  setInitialFilters,
  getOptionFromDropdownValue
) => {
  try {
    // Step 1: Get current filter model from AG Grid (source of truth)
    const currentFilterModel = api?.getFilterModel() || {};

    // Step 2: Convert all active filters to modal format
    const filtersFromModel = convertActiveFiltersToModalFormat(
      currentFilterModel,
      api,
      colDefs
    );

    // Step 3: Prepare value based on column type (uses strategy pattern)
    const valueForModal = prepareValueForModal(
      inputValue,
      columnDef,
      getOptionFromDropdownValue
    );

    // Step 4: Update or add the filter
    const updatedFilters = updateOrAddFilter(
      filtersFromModel,
      currentColumnId,
      valueForModal,
      columnDef,
      colDefs
    );

    // Step 5: Update modal state
    setValue?.("filters", updatedFilters);
    setInitialFilters?.(updatedFilters);
  } catch (err) {
    console.warn("Error syncing modal filters:", err);
  }
};

/**
 * Processes advance search filters and builds AG Grid filter model
 * @param {array} formValues - Array of filter objects from form
 * @param {array} colDefs - Column definitions
 * @returns {object} - AG Grid filter model
 */
export const buildAdvanceSearchFilterModel = (formValues, colDefs) => {
  const filterModel = {};

  if (!formValues || !Array.isArray(formValues)) {
    return filterModel;
  }

  formValues.forEach((value) => {
    if (!value?.column?.value || !value?.operation?.value) {
      return;
    }

    const columnDef = findColumnDef(colDefs, value.column.value);
    if (!columnDef) {
      return;
    }

    const { parsedValue, filterType } = parseFilterValue(
      value.value,
      columnDef
    );

    if (!isValidFilterValue(parsedValue)) {
      return;
    }

    const columnKey = columnDef.colId || value.column.value;
    const filterEntry = buildFilterModelEntry(
      parsedValue,
      filterType,
      columnDef,
      value.operation.value
    );

    if (filterEntry) {
      filterModel[columnKey] = filterEntry;
    }
  });

  return filterModel;
};
