import { OperatorType, OperatorTypes } from '@unito/integration-api';
import { Request, Response, NextFunction } from 'express';

declare global {
  // eslint-disable-next-line @typescript-eslint/no-namespace
  namespace Express {
    interface Locals {
      filters: Filter[];
    }
  }
}

/**
 * Represents one filter parsed from the filters query param.
 *
 *  Given a filters query param containing:
 *  `filters=[...],name=John,[...]`
 *
 *  Filter for `name=John` will be:
 * `{ field: 'name', operator: 'EQUAL', values: ['John'] }`
 */
export type Filter = {
  field: string;
  operator: OperatorType;
  // Without the schema of the item,
  // we can't determine the types of the values (number, boolean, etc).
  values: string[] | undefined;
};

// The operators are ordered by their symbol length, in descending order.
// This is necessary because the symbol of an operator can be
// a subset of the symbol of another operator.
//
// For example, the symbol "=" (EQUAL) is a subset of the symbol "!=" (NOT_EQUAL).
const ORDERED_OPERATORS = Object.values(OperatorTypes).sort((o1, o2) => o2.length - o1.length);

function extractFilters(req: Request, res: Response, next: NextFunction) {
  const rawFilters = req.query.filter;

  res.locals.filters = [];

  if (typeof rawFilters === 'string') {
    for (const rawFilter of rawFilters.split(',')) {
      for (const operator of ORDERED_OPERATORS) {
        if (rawFilter.includes(operator)) {
          const [field, valuesRaw] = rawFilter.split(operator, 2);
          const values = valuesRaw ? valuesRaw.split('|').map(decodeURIComponent) : [];

          res.locals.filters.push({ field: field!, operator, values });

          break;
        }
      }
    }
  }

  next();
}

export default extractFilters;
