import { MatcherFactory } from './matcher';

import { numberFields, dateFields, literalFields, termSpaceToImageField, defaultField } from './fields';
import { FieldName, FieldMatcher, RangeEqualQualifier, TermType, AstMatcher } from './types';

type RangeInfo = [FieldName, RangeEqualQualifier, TermType];

function normalizeTerm(term: string, wildcardable: boolean) {
  if (!wildcardable) {
    return term.replace('\\"', '"');
  }
  return term.replace(/\\([^*?])/g, '$1');
}

function parseRangeField(field: string): RangeInfo | null {
  if (numberFields.indexOf(field) !== -1) {
    return [field, 'eq', 'number'];
  }

  if (dateFields.indexOf(field) !== -1) {
    return [field, 'eq', 'date'];
  }

  const qual = /^(\w+)\.([lg]te?|eq)$/.exec(field);

  if (qual) {
    const fieldName: FieldName = qual[1];
    const rangeQual = qual[2] as RangeEqualQualifier;

    if (numberFields.indexOf(fieldName) !== -1) {
      return [fieldName, rangeQual, 'number'];
    }

    if (dateFields.indexOf(fieldName) !== -1) {
      return [fieldName, rangeQual, 'date'];
    }
  }

  return null;
}

function makeTermMatcher(term: string, fuzz: number, factory: MatcherFactory): [FieldName, FieldMatcher] {
  let rangeParsing, candidateTermSpace, termCandidate;
  let localTerm = term;
  const wildcardable = fuzz === 0 && !/^"([^"]|\\")+"$/.test(localTerm);

  if (!wildcardable && !fuzz) {
    // Remove quotes around quoted literal term
    localTerm = localTerm.substring(1, localTerm.length - 1);
  }

  localTerm = normalizeTerm(localTerm, wildcardable);

  // N.B.: For the purposes of this parser, boosting effects are ignored.
  const matchArr = localTerm.split(':');

  if (matchArr.length > 1) {
    candidateTermSpace = matchArr[0];
    termCandidate = matchArr.slice(1).join(':');
    rangeParsing = parseRangeField(candidateTermSpace);

    if (rangeParsing) {
      const [fieldName, rangeType, fieldType] = rangeParsing;

      if (fieldType === 'date') {
        return [fieldName, factory.makeDateMatcher(termCandidate, rangeType)];
      }

      return [fieldName, factory.makeNumberMatcher(parseFloat(termCandidate), fuzz, rangeType)];
    } else if (literalFields.indexOf(candidateTermSpace) !== -1) {
      return [candidateTermSpace, factory.makeLiteralMatcher(termCandidate, fuzz, wildcardable)];
    } else if (candidateTermSpace === 'my') {
      return [candidateTermSpace, factory.makeUserMatcher(termCandidate)];
    }
  }

  return [defaultField, factory.makeLiteralMatcher(localTerm, fuzz, wildcardable)];
}

export function getAstMatcherForTerm(term: string, fuzz: number, factory: MatcherFactory): AstMatcher {
  const [fieldName, matcher] = makeTermMatcher(term, fuzz, factory);

  return (e: HTMLElement) => {
    const value = e.getAttribute(termSpaceToImageField[fieldName]) || '';
    const documentId = parseInt(e.getAttribute(termSpaceToImageField.id) || '0', 10);
    return matcher(value, fieldName, documentId);
  };
}