// Copyright (C) 2025 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * Utility functions for the query builder.
 */

import {
  type PerfettoSqlType,
  isQuantitativeType,
  typesEqual,
} from '../../../trace_processor/perfetto_sql_type';
import type {ColumnInfo} from './column_info';

/**
 * Checks if a column type is numeric/quantitative.
 * Numeric types include: int, double, duration, timestamp, boolean, id,
 * joinid, and arg_set_id.
 *
 * @param type The PerfettoSqlType to check
 * @returns true if the type is numeric
 */
export function isNumericType(type?: PerfettoSqlType): boolean {
  if (type === undefined) return false;
  return isQuantitativeType(type);
}

/**
 * Checks if a column type is a string type.
 *
 * @param type The PerfettoSqlType to check
 * @returns true if the type is a string
 */
export function isStringType(type?: PerfettoSqlType): boolean {
  if (type === undefined) return false;
  return type.kind === 'string';
}

/**
 * Checks if a column is compatible with a specific aggregation operation.
 *
 * @param col The column to check (must have an optional `type` field)
 * @param col.type The SQL type of the column
 * @param op The aggregation operation (e.g., 'SUM', 'COUNT', 'MEAN', etc.)
 * @returns true if the column is compatible with the operation
 */
export function isColumnValidForAggregation(
  col: {type?: PerfettoSqlType},
  op?: string,
): boolean {
  if (!op) return true;

  const isNumeric = isNumericType(col.type);
  const isString = isStringType(col.type);

  switch (op) {
    case 'MEAN':
    case 'MEDIAN':
    case 'PERCENTILE':
    case 'DURATION_WEIGHTED_MEAN':
      // These operations require numeric types
      return isNumeric;
    case 'GLOB':
      // GLOB requires string types
      return isString;
    case 'COUNT':
    case 'COUNT(*)':
    case 'SUM':
    case 'MIN':
    case 'MAX':
    default:
      // These operations work on all types
      return true;
  }
}

/**
 * Gets a human-readable description of the type requirements for an aggregation operation.
 *
 * @param op The aggregation operation
 * @returns A description of the type requirements
 */
export function getAggregationTypeRequirements(op: string): string {
  switch (op) {
    case 'MEAN':
    case 'MEDIAN':
    case 'PERCENTILE':
    case 'DURATION_WEIGHTED_MEAN':
      return 'Requires numeric column';
    case 'GLOB':
      return 'Requires string column';
    case 'COUNT(*)':
      return 'No column required';
    case 'COUNT':
    case 'SUM':
    case 'MIN':
    case 'MAX':
      return 'Works with any column type';
    default:
      return 'Unknown operation';
  }
}

export interface GetCommonColumnsOptions {
  // Column names to exclude from the result
  excludedColumns?: Set<string>;
  // Column types to exclude from the result
  excludedTypes?: Set<PerfettoSqlType['kind']>;
}

/**
 * Finds columns that exist in all provided column arrays.
 * Returns the intersection of column names, optionally filtered by exclusions.
 *
 * @param columnArrays Array of ColumnInfo arrays to find common columns across
 * @param options Optional exclusion filters for column names and types
 * @returns Sorted array of common column names
 */
export function getCommonColumns(
  columnArrays: ColumnInfo[][],
  options?: GetCommonColumnsOptions,
): string[] {
  if (columnArrays.length === 0) {
    return [];
  }

  const excludedColumns = options?.excludedColumns ?? new Set();
  const excludedTypes = options?.excludedTypes ?? new Set();

  const isTypeExcluded = (type?: PerfettoSqlType): boolean => {
    if (type === undefined) return false;
    return excludedTypes.has(type.kind);
  };

  // Start with columns from the first array
  const firstArray = columnArrays[0];
  const commonColumns = new Set(
    firstArray
      .filter((c) => !excludedColumns.has(c.name) && !isTypeExcluded(c.type))
      .map((c) => c.name),
  );

  // Intersect with columns from remaining arrays
  for (let i = 1; i < columnArrays.length; i++) {
    const colsMap = new Map(columnArrays[i].map((c) => [c.name, c.type]));
    for (const col of commonColumns) {
      const colType = colsMap.get(col);
      if (colType === undefined || isTypeExcluded(colType)) {
        commonColumns.delete(col);
      }
    }
  }

  return Array.from(commonColumns).sort();
}

/**
 * Checks if two PerfettoSqlType values are equal.
 * Convenience re-export for use in the query builder.
 */
export {typesEqual};
