/*
 * Copyright (C) 2020 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {generateTemplate, getProjectReflection, settings} from '../../src';
import path from 'path';
import {expect} from 'chai';
import {ProjectReflection} from 'typedoc';
import {MapAggTestOptions, MinimalMappingDescription} from './map-agg-test-options';
import type {AggregationSchema, ESNestedAggregation} from '../../schema/aggregations';
import type {ElasticsearchTemplateCollection} from '../../schema/mappings';

export class MapAggTest {
  mapping_model_path!: string;

  reflection!: ProjectReflection;

  constructor(directory: string) {
    // eslint-disable-next-line unicorn/prefer-module
    this.mapping_model_path = path.resolve(__dirname, directory);
    this.reflection = getProjectReflection(this.mapping_model_path);
  }

  testInterfaceAgainstPath(options: MapAggTestOptions) {
    const template = generateTemplate(this.reflection, options.ignoredTags ?? [], false, [options.name]);

    if (options.err === undefined) {
      expect(template.errors).to.be.deep.equal([], 'Unexpected Error!');
    } else {
      for (const error of template.errors) {
        expect(options.err).to.include(error, 'Unexpected Error!');
      }
    }

    if (options.agg !== undefined) {
      const expectedAggSchema = MapAggTest.buildAggregation(
        options.name,
        options.agg.fields,
        options.agg.globals,
      );
      expect(template.aggregations).to.be.deep.equal(expectedAggSchema, 'Aggregation schema not equal!');
    }

    if (options.map !== undefined) {
      const expectedMappingSchema = MapAggTest.buildMapping(options.name, options.map);
      expect(template.mappings).to.be.deep.equal(expectedMappingSchema, 'Mapping schema not equal!');
    }
  }

  static buildAggregation(name: string, fields?: string[], globals?: string[]): AggregationSchema {
    const out: AggregationSchema = {
      '@all': {
        aggs: {},
        filter: {
          match_all: {},
        },
      },
    };

    for (const global of globals ?? []) {
      (out['@all']! as ESNestedAggregation).aggs[global] = {
        terms: {
          field: `${global}.raw`,
          size: 1000,
        },
      };
    }

    if (fields === undefined || fields.length === 0) {
      return out;
    }

    out[name] = {
      aggs: {},
      filter: {
        term: {
          type: name,
        },
      },
    };

    for (const field of fields) {
      (out[name]! as ESNestedAggregation).aggs[field] = {
        terms: {
          field: `${field}.raw`,
          size: 1000,
        },
      };
    }

    return out;
  }

  static buildMapping(name: string, map: MinimalMappingDescription): ElasticsearchTemplateCollection {
    let typeNameWithoutSpaces = name.toLowerCase();
    while (typeNameWithoutSpaces.includes(' ')) {
      typeNameWithoutSpaces = typeNameWithoutSpaces.replace(' ', '_');
    }

    const out: ElasticsearchTemplateCollection = {};
    const templateName = `template_${typeNameWithoutSpaces}`;
    out[templateName] = {
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore empty object
      mappings: {},
      settings: settings,
      index_patterns: [`stapps_${typeNameWithoutSpaces}*`],
    };
    const maps = map.maps ?? {};
    maps.type = {
      type: 'text',
    };
    maps.creation_date = {
      type: 'date',
    };
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore can be used to index
    out[templateName].mappings = {
      _source: {
        excludes: ['creation_date'],
      },
      date_detection: false,
      dynamic: 'strict',
      properties: maps,
      dynamic_templates: map.dynamicTemplates ?? [],
    };

    return out;
  }
}
