/* eslint-disable no-useless-catch */
import mongoose from 'mongoose';
import { ITEM_SHORTCUTS } from '{{PATH_TO_SOURCE_DIR}}constants/tracking_id.constants';
import config from '{{PATH_TO_SOURCE_DIR}}config';
import { AppBaseService } from '{{PATH_TO_SOURCE_DIR}}app.base.service';
import { getRegistry } from '{{PATH_TO_SOURCE_DIR}}main';

import userCan from '{{PATH_TO_SOURCE_DIR}}guards';
import { Service, Inject, Container } from 'typedi';
import initLogger, { JsmLogger } from 'jsm-logger';
import { 
  fixFiltersObject,
  slugify,
  ObjectUpdatedProperties,
  createTrackingId,
  defaults,
  deepSanitizeObjectValues,
} from "jsm-utilities";
import { 
  createBooleanFilter,
  createDateRangeFilter,
  createStringFilter,
  createNumberFilter,
} from "jsm-utilities/dist/node";
import exceptions from "jsm-exceptions";
import { uniq } from "lodash";
import { 
  EventDispatcher, 
  mapDocumentToExposed,
  _FileAttributeSchemaFields,
  getUserSnapshot,
} from "jsm-core";
import * as {{SUB_MODULE_ENTITY_CAMELCASED_SINGULAR}}Sanitizers from './{{SUB_MODULE_DOT_ENTITY_KEBABCASED_SINGULAR}}.sanitizers';
import * as {{SUB_MODULE_ENTITY_CAMELCASED_SINGULAR}}Validators from './{{SUB_MODULE_DOT_ENTITY_KEBABCASED_SINGULAR}}.validators';
{{#if EVENTS_OBJECT_NAME}}
import {{EVENTS_OBJECT_NAME}} from './{{SUB_MODULE_DOT_ENTITY_KEBABCASED_SINGULAR}}.events';
{{/if}}



type EntityAlias = {{orAny ENTITY_TYPING_ALIAS}};
type DocumentAlias = {{orAny MONGOOSE_DOCUMENT_TYPING_ALIAS}};
type DocumentProperties = Jsm.Core.Utils.DocumentProperties<EntityAlias>;


/**
 * @service {{MODULE.SERVICE.CLASS_NAME}}
 * @description {{MODULE.SERVICE.DESCRIPTION}}
 */
@Service()
export class {{MODULE.SERVICE.CLASS_NAME}} extends AppBaseService {
  readonly ENTITY = "{{SUB_MODULE_ENTITY_CAMELCASED_CAPITALIZED_SINGULAR}}" as const;

  constructor(
    {{#each MODULE.SERVICE.INJECTIONS}}
    @Inject('{{this.name}}') public {{this.name}}: {{this.type}},
    {{/each}}
    @EventDispatcher() private eventDispatcher: EventDispatcher
  ) {
    super();
  }

  {{!-- generate logic --}}
  {{#if ENTITY_META.has_slug}}
  {{>method-generateSlug .}}
  {{/if}}

  {{!-- generate searchMeta --}}
  {{#if ENTITY_META.has_search_meta}}
  {{>method-generateSearchMeta .}}
  {{/if}}
  
  {{!-- generateSnapshotsObject --}}
  {{#if ENTITY_META.has_snapshots}}
  {{>method-generateSnapshotsObject .}}
  {{/if}}

  {{!-- applyAuthDataBasedFilters --}}
  {{>method-applyAuthDataBasedFilters .}}

  {{!-- applyFilters --}}
  {{>method-applyFilters .}}


  {{#each MODULE.API.METHODS}}
    {{#each this.meta.links}}
  /**
   * {{serviceMethodComments ../this this}}
   */
  async {{this.fires.method}}({{serviceMethodArgs ../this this}}): Promise<{{orAny ../this.responseFullName}}> {
    const scenario = this.initScenario(this.logger, this.{{this.fires.method}},)
    try {
      this.logger.event('Calling method {{this.fires.method}}');
      {{!-- options --}}
      {{serviceMethodOptions ../this}}

      let result: {{orAny ../this.responseFullName}} = {};
      
      {{#if ../this.meta.has_custom_logic}}
      /**
       * Custom logic
       */
      // TODO: Add your custom logic here
      {{customLogic ../this}}
      
      {{else}}
      {{!-- body --}}
      {{!-- sanitizer --}}
      {{#ifMethodHasSanitizer ../this}}
      /**
       * Sanitize the body
       */
      body = await {{../../this.SUB_MODULE_ENTITY_CAMELCASED_SINGULAR}}Sanitizers.sanitize{{../this.name}}Body(body, authData, opt);
      const {data} = body;
      {{/ifMethodHasSanitizer}}
      {{!-- validator --}}
      {{#ifMethodHasValidator ../this}}
      /**
       * Validate the body
       */
      const {error} = await {{../../this.SUB_MODULE_ENTITY_CAMELCASED_SINGULAR}}Validators.validate{{../this.name}}Body(body, authData, opt);
      if (error) throw error;
      {{/ifMethodHasValidator}}

      {{!-- create logic --}}
      {{#ifIsCreateMethod ../this this}}
      {{>method-create ../../this METHOD=../this LINK=this}}
      {{/ifIsCreateMethod}}

      {{!-- update logic --}}
      {{#ifIsStrictUpdateMethod ../this this}}
      {{>method-update ../../this METHOD=../this LINK=this}}
      {{/ifIsStrictUpdateMethod}}
      
      {{!-- deleteOne logic --}}
      {{#ifIsDeleteOneMethod ../this this}}
      {{>method-deleteOne ../../this METHOD=../this LINK=this}}
      {{/ifIsDeleteOneMethod}}
      
      {{!-- restoreOne logic --}}
      {{#ifIsRestoreOneMethod ../this this}}
      {{>method-restoreOne ../../this METHOD=../this LINK=this}}
      {{/ifIsRestoreOneMethod}}
      
      {{!-- deleteMany logic --}}
      {{#ifIsDeleteManyMethod ../this this}}
      {{>method-deleteMany ../../this METHOD=../this LINK=this}}
      {{/ifIsDeleteManyMethod}}
      
      {{!-- getOne logic --}}
      {{#ifIsGetOneMethod ../this this}}
      {{>method-getOne ../../this METHOD=../this LINK=this}}
      {{/ifIsGetOneMethod}}
      
      {{!-- getMany logic --}}
      {{#ifIsGetManyMethod ../this this}}
      {{>method-getMany ../../this METHOD=../this LINK=this}}
      {{/ifIsGetManyMethod}}

      {{!-- emit event --}}
      {{#if ../this.meta.emited_event.name_path}}
      {{#ifIsDeleteManyMethod ../this this}}
      /**
       * Trigger the event before the method is called
       */
      {{else}}
      /**
       * Trigger the event before the method is called
       */
      this.eventDispatcher.dispatch<{{../this.meta.emited_event.payload_typing_alias}}>(
        {{../this.meta.emited_event.name_path}},
        { data: mapDocumentToExposed(doc) }
      );
      {{/ifIsDeleteManyMethod}}
      {{/if}}
      {{/if}}

      scenario.end();
      return result;
    } catch (error) {
      scenario.error(error);
      throw error;
    }
  }
  {{nl}}
    {{/each}}
  {{/each}}

}
