import { TranslatedPropertyHelper } from './../utils/translated-property-helper';
import { inject } from 'aurelia-framework';
import { I18N } from 'aurelia-i18n';
import * as breeze from 'breeze-client';
import moment from 'moment';
import { CustomLogger } from '../modules/custom-logger';
import { Datacontext } from './data-context';
import { ValidationRules } from 'aurelia-validation';

@inject(I18N, CustomLogger, Datacontext, TranslatedPropertyHelper)
export abstract class BreezeModel {
  constructor(public i18N: I18N, public logger: CustomLogger, public datacontext: Datacontext, private translatedPropertyHelper: TranslatedPropertyHelper) { }

  private addValidationRules(entityType: breeze.EntityType | breeze.ComplexType): void {
    if (entityType) {
      entityType.dataProperties.forEach((dp) => {
        let castDp = <any>dp;
        if (castDp.custom != null && castDp.custom.length > 0) {
          castDp.custom.forEach(validator => {
            if (validator.Name == "RegularExpressionAttribute") {
              let regexValidator = breeze.Validator.makeRegExpValidator(
                `regularExpressionValidator-${entityType.name}-${dp.name}"`,
                validator.ValidatorParam,
                this.i18N.tr("validation.regex", { ns: "common" }));
              // Register it with the breeze Validator class.
              breeze.Validator.register(regexValidator);
              dp.validators.push(regexValidator);
            }
            else if (validator.Name == "DateGreaterThanAttribute") {
              if ((validator.ValidatorParam as string).length > 0) {
                let startDateName: string = (validator.ValidatorParam as string).charAt(0).toLowerCase() + validator.ValidatorParam.slice(1);
                let parentName: string = dp.parentType.shortName.toLocaleLowerCase();
                let dateGreaterThanValidator = new breeze.Validator(
                  `dateGreaterThanValidator-${entityType.name}-${dp.name}"`,
                  (value, context) => {
                    let startDate: Date = (context as any).entity.getProperty(startDateName);
                    let endDate: Date = value;
                    if (endDate != null) {
                      return startDate < endDate;
                    }
                    return true;
                  },
                  {
                    messageTemplate: this.i18N.tr("validation.mustBeGreaterThan", { ns: "common", otherPropertyName: this.i18N.tr(parentName + "." + startDateName) })
                  });
                breeze.Validator.register(dateGreaterThanValidator);
                dp.validators.push(dateGreaterThanValidator);
              }
            }
            else if (validator.Name == "EmailAddressAttribute") {
              dp.validators.push(breeze.Validator.emailAddress());
            }
          });
        }
      });
      this.logger.Log('Validation rules added', entityType.name, null, false);
    }
  }

  public createNullo(entityName: string, manager: breeze.EntityManager, values?: any) {
    var initialValues = values
      || { name: ' [Select a ' + entityName.toLowerCase() + ']' };
    return manager.createEntity(entityName, initialValues, breeze.EntityState.Unchanged);
  }

  private forceMetaDataCustomProperties(entityType: breeze.EntityType | breeze.ComplexType): void {
    entityType.dataProperties.forEach(dp => {
      
      let customIsUnmapped = this.getCustomMetadataPropertyValue(dp, "isUnmapped");
      let customIsSettable = this.getCustomMetadataPropertyValue(dp, "isSettable");
      let customIsDatabaseGenerated = this.getCustomMetadataPropertyValue(dp, "isDatabaseGenerated");
        
      if (customIsSettable == false && customIsDatabaseGenerated == true) {
        dp.isUnmapped = false;
      } else if (customIsSettable == false) {
        dp.isUnmapped = true;
      } else if (customIsUnmapped != null && customIsUnmapped != dp.isUnmapped) {
        dp.isUnmapped = customIsUnmapped;
      }

      if (dp.isUnmapped && !entityType.unmappedProperties.some(p => dp.name == p.name)) entityType.unmappedProperties.push(dp);
    });
  }

  private getCustomMetadataPropertyValue(dp: breeze.DataProperty, name: string): boolean | null {
    if (Array.isArray(dp?.custom)) {
      return (<Array<any>>dp.custom).find(x => x[name] != null)?.[name];
    }

    return null;
  }

  public configureMetadataStore(metadataStore: breeze.MetadataStore, manager: breeze.EntityManager): void {
    function qualifyURL(url) {
      let a = document.createElement('a');
      a.href = url;
      return (<HTMLAnchorElement>a.cloneNode(false)).href;
    };
    this.configureInitializerAndCtor(metadataStore, manager);


    // Add the validation rules in all entity types if needed
    var allEntityTypes = metadataStore.getEntityTypes();
    allEntityTypes.forEach(entityType => {
      this.forceMetaDataCustomProperties(entityType);
      this.addValidationRules(entityType);

      var ctorRegistry = (<any>metadataStore)._ctorRegistry;
      var initializerFn = (entity: breeze.Entity) => {
        //this.addToJsonFunction(entity);
        if ((<any>entity).isSelected == null)
          (<any>entity).isSelected = false;
        this.detectTranslatedProperties(entityType, entity);
        this.initializeCommonEntities(entityType, entity);
      }

      if (ctorRegistry[entityType.name] == null) {
        metadataStore.registerEntityTypeCtor(entityType.name, null, initializerFn);
      } else {
        var existingInitializerFn = ctorRegistry[entityType.name].initFn;
        var newInitFn = (entity) => {
          initializerFn(entity);
          if (existingInitializerFn != null)
            existingInitializerFn(entity);
        }
        ctorRegistry[entityType.name].initFn = newInitFn;
        (<any>entityType).getCtor(true);
      }
    });
    this.addCustomValidationRules();

  }
  private addCustomValidationRules() {
    ValidationRules.customRule('validate-date', (val, obj, format) => {
      let isNullOrEmpty = function (arg: string): boolean {
        if (arg == null || arg === "")
          return true;
        return false;
      };
      var aDate = moment(val, format, true);
      let invalidDate = !isNullOrEmpty(val) && !aDate.isValid();
      return !invalidDate;
    }, this.i18N.tr("validation.invalidDate", { ns: "common" }));
  }

  abstract configureInitializerAndCtor(metadataStore: breeze.MetadataStore, manager: breeze.EntityManager);

  initializeCommonEntities(entityType: breeze.EntityType | breeze.ComplexType, entity: breeze.Entity) {
    if (entityType.shortName == 'Language') {
      Object.defineProperty(entity, '_columnName', {
        get: () => {
          return "lang" + (entity as any).columnIndex;
        }
      });
      Object.defineProperty(entity, '_flag', {
        get: () => {
          let entityAny = entity as any;
          if (entityAny.code == 'en') return 'gb';
          else return entityAny.code;
        }
      });
    }
  }

  detectTranslatedProperties(entityType: breeze.EntityType | breeze.ComplexType, entity: breeze.Entity): any {
    let translatedProperties = entityType.complexProperties.filter(cp => (<any>cp.dataType).shortName == 'TranslatedProperty');
    if (translatedProperties != null && translatedProperties.length > 0) {

      translatedProperties.forEach(prop => {
        if (entity[prop.name]?.hasOwnProperty('_translation')) {
          return;
        }
        this.translatedPropertyHelper.addNecessaryProperties(entity[prop.name], true);
      });
    }
  }
}
