import {expect} from 'chai';
import {
  ANDSearchQuery,
  DistanceSearch,
  FromDateSearch,
  MaxRatingSearch,
  MaxResolutionSearch,
  MinRatingSearch,
  MinResolutionSearch,
  OrientationSearch,
  ORSearchQuery,
  RangeSearch,
  SearchQueryDTO,
  SearchQueryTypes,
  SomeOfSearchQuery,
  TextSearch,
  TextSearchQueryMatchTypes,
  ToDateSearch
} from '../../../src/common/entities/SearchQueryDTO';
import {defaultQueryKeywords, SearchQueryParser} from '../../../src/common/SearchQueryParser';


describe('SearchQueryParser', () => {

  const check = (query: SearchQueryDTO) => {
    const parser = new SearchQueryParser(defaultQueryKeywords);
    expect(parser.parse(parser.stringify(query))).to.deep.equals(query, parser.stringify(query));

  };

  describe('should serialize and deserialize', () => {
    it('Text search', () => {
      check({type: SearchQueryTypes.any_text, text: 'test'} as TextSearch);
      check({type: SearchQueryTypes.person, text: 'person_test'} as TextSearch);
      check({type: SearchQueryTypes.directory, text: 'directory'} as TextSearch);
      check({type: SearchQueryTypes.directory, text: '2000.10.15 (Some event)'} as TextSearch);
      check({
        type: SearchQueryTypes.directory,
        text: '2000.10.15 (Some event)',
        matchType: TextSearchQueryMatchTypes.exact_match
      } as TextSearch);
      check({type: SearchQueryTypes.directory, text: '2000.10.15 (Some event) '} as TextSearch);
      check({type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch);
      check({type: SearchQueryTypes.caption, text: 'caption'} as TextSearch);
      check({type: SearchQueryTypes.file_name, text: 'filename'} as TextSearch);
      check({type: SearchQueryTypes.position, text: 'New York'} as TextSearch);
      check({
        type: SearchQueryTypes.position,
        matchType: TextSearchQueryMatchTypes.exact_match,
        text: 'New York'
      } as TextSearch);
      check({
        type: SearchQueryTypes.position,
        matchType: TextSearchQueryMatchTypes.exact_match,
        negate: true,
        text: 'New York'
      } as TextSearch);

      check({type: SearchQueryTypes.any_text, text: 'test', negate: true} as TextSearch);
    });

    it('Date search', () => {
      check({type: SearchQueryTypes.from_date, value: (Date.UTC(2020, 1, 10))} as FromDateSearch);
      check({type: SearchQueryTypes.from_date, value: (Date.UTC(2020, 1, 1))} as FromDateSearch);
      check({type: SearchQueryTypes.to_date, value: (Date.UTC(2020, 1, 20))} as ToDateSearch);
      check({type: SearchQueryTypes.to_date, value: (Date.UTC(2020, 1, 1))} as ToDateSearch);
      check({type: SearchQueryTypes.from_date, value: (Date.UTC(2020, 1, 1)), negate: true} as FromDateSearch);
      check({type: SearchQueryTypes.to_date, value: (Date.UTC(2020, 1, 1)), negate: true} as ToDateSearch);

      const parser = new SearchQueryParser(defaultQueryKeywords);
      // test if date gets simplified on 1st of Jan.
      let query: RangeSearch = {type: SearchQueryTypes.to_date, value: (Date.UTC(2020, 0, 1))} as ToDateSearch;
      expect(parser.parse(defaultQueryKeywords.to + ':' + (new Date(query.value)).getFullYear()))
        .to.deep.equals(query, parser.stringify(query));

      query = ({type: SearchQueryTypes.from_date, value: (Date.UTC(2020, 0, 1))} as FromDateSearch);
      expect(parser.parse(defaultQueryKeywords.from + ':' + (new Date(query.value)).getFullYear()))
        .to.deep.equals(query, parser.stringify(query));

    });
    it('Rating search', () => {
      check({type: SearchQueryTypes.min_rating, value: 10} as MinRatingSearch);
      check({type: SearchQueryTypes.max_rating, value: 1} as MaxRatingSearch);
      check({type: SearchQueryTypes.min_rating, value: 10, negate: true} as MinRatingSearch);
      check({type: SearchQueryTypes.max_rating, value: 1, negate: true} as MaxRatingSearch);
    });
    it('Resolution search', () => {
      check({type: SearchQueryTypes.min_resolution, value: 10} as MinResolutionSearch);
      check({type: SearchQueryTypes.max_resolution, value: 5} as MaxResolutionSearch);
      check({type: SearchQueryTypes.min_resolution, value: 10, negate: true} as MinResolutionSearch);
      check({type: SearchQueryTypes.max_resolution, value: 5, negate: true} as MaxResolutionSearch);
    });
    it('Distance search', () => {
      check({type: SearchQueryTypes.distance, distance: 10, from: {text: 'New York'}} as DistanceSearch);
      check({type: SearchQueryTypes.distance, distance: 10, from: {text: 'New York'}, negate: true} as DistanceSearch);
    });
    it('OrientationSearch search', () => {
      check({type: SearchQueryTypes.orientation, landscape: true} as OrientationSearch);
      check({type: SearchQueryTypes.orientation, landscape: false} as OrientationSearch);
    });
    it('Default logical operator should be AND', () => {

      const parser = new SearchQueryParser(defaultQueryKeywords);
      expect(parser.parse('a b')).to.deep.equals({
        type: SearchQueryTypes.AND,
        list: [
          {type: SearchQueryTypes.any_text, text: 'a'} as TextSearch,
          {type: SearchQueryTypes.any_text, text: 'b'} as TextSearch
        ]
      } as ANDSearchQuery);
    });
    it('And search', () => {
      check({
        type: SearchQueryTypes.AND,
        list: [
          {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
          {type: SearchQueryTypes.position, text: 'New York'} as TextSearch
        ]
      } as ANDSearchQuery);

      check({
        type: SearchQueryTypes.AND,
        list: [
          {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
          {
            type: SearchQueryTypes.position,
            matchType: TextSearchQueryMatchTypes.exact_match,
            text: 'New York'
          } as TextSearch
        ]
      } as ANDSearchQuery);
      check({
        type: SearchQueryTypes.AND,
        list: [
          {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
          {type: SearchQueryTypes.caption, text: 'caption'} as TextSearch,
          {type: SearchQueryTypes.position, text: 'New York'} as TextSearch
        ]
      } as ANDSearchQuery);
      check({
        type: SearchQueryTypes.AND,
        list: [
          {
            type: SearchQueryTypes.SOME_OF,
            min: 2,
            list: [
              {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
              {type: SearchQueryTypes.position, text: 'New York'} as TextSearch,
              {type: SearchQueryTypes.caption, text: 'caption test'} as TextSearch
            ]
          } as SomeOfSearchQuery,
          {type: SearchQueryTypes.position, text: 'New York'} as TextSearch
        ]
      } as ANDSearchQuery);
    });
    it('Or search', () => {
      check({
        type: SearchQueryTypes.OR,
        list: [
          {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
          {type: SearchQueryTypes.position, text: 'New York'} as TextSearch
        ]
      } as ORSearchQuery);
      check({
        type: SearchQueryTypes.OR,
        list: [
          {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
          {type: SearchQueryTypes.person, text: 'person_test'} as TextSearch,
          {type: SearchQueryTypes.position, text: 'New York'} as TextSearch
        ]
      } as ORSearchQuery);
    });
    it('Some of search', () => {
      check({
        type: SearchQueryTypes.SOME_OF,
        list: [
          {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
          {type: SearchQueryTypes.position, text: 'New York'} as TextSearch
        ]
      } as SomeOfSearchQuery);
      check({
        type: SearchQueryTypes.SOME_OF,
        list: [
          {
            type: SearchQueryTypes.keyword,
            matchType: TextSearchQueryMatchTypes.exact_match,
            text: 'big boom'
          } as TextSearch,
          {
            type: SearchQueryTypes.position,
            matchType: TextSearchQueryMatchTypes.exact_match,
            text: 'New York'
          } as TextSearch,
        ]
      } as SomeOfSearchQuery);
      check({
        type: SearchQueryTypes.SOME_OF,
        min: 2,
        list: [
          {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
          {type: SearchQueryTypes.position, text: 'New York'} as TextSearch,
          {type: SearchQueryTypes.caption, text: 'caption test'} as TextSearch
        ]
      } as SomeOfSearchQuery);
      check({
        type: SearchQueryTypes.SOME_OF,
        min: 2,
        list: [
          {type: SearchQueryTypes.keyword, text: 'big boom'} as TextSearch,
          {type: SearchQueryTypes.person, text: 'person_test'} as TextSearch,
          {
            type: SearchQueryTypes.AND,
            list: [
              {type: SearchQueryTypes.caption, text: 'caption'} as TextSearch,
              {type: SearchQueryTypes.position, text: 'New York'} as TextSearch
            ]
          } as ANDSearchQuery
        ]
      } as SomeOfSearchQuery);
    });
  });


});