import { UserBaseForm } from './vm/security/users/user-base-form';
import { UIAlert } from './ui/shared/ui-alert';
import { CommunicationHubClient } from './services/signalr/communication-hub-client';
import { BreezeSyncHubClient } from './services/signalr/breeze-sync-hub-client';
import { SecurityHubClient } from './services/signalr/security-hub-client';
import { UIBadge } from './attributes/ui-badge';
import { UINotificationList } from './ui/chat/ui-notification-list';
import { UINotification } from './ui/chat/ui-notification';
import { Avatar } from './ui/history/avatar';
import { AppRouter } from 'aurelia-router';
import { UIEntityHistory } from './ui/history/ui-entity-history';
import { UIFilesComponents } from './ui/files/ui-files';
import { computedFrom, Container, FrameworkConfiguration } from "aurelia-framework";
import { PLATFORM } from 'aurelia-pal';
import { AuthConfig, Config } from "./config";
// import "./libs/array";
// import "./libs/string";
// import "./libs/window";
import "./locales/common-fr.json";
import "./locales/common-nl.json";
import "./locales/common-en.json";
import { BreezeModel } from "./data/breeze-model";
import { ValueConverters } from './value-converters/value-converters';
import { MenuComponents } from "./ui/menu/ui-navbar";
import { Page } from "./ui/page/ui-page";
import { UIDialogHeader } from "./ui/page/ui-dialog-header";
import { UISvgIcon } from "./ui/icons/ui-svg-icon";
import { UIIcon } from './ui/icons/ui-icon';
import { UIFlag } from './ui/icons/ui-flag';
import * as AttributeHelpers from './attributes/ui-helpers';
import { UITooltip } from './attributes/ui-tooltip';
import { UIContextmenu } from './attributes/ui-contextmenu';
import { UIInputmaskCustomAttribute } from './attributes/ui-inputmask';
import { Forms } from './ui/forms/ui-forms';
import { Panels } from './ui/panels/ui-panels';
import { Menus } from './ui/menu/ui-menus';
import { Shared } from './ui/shared/ui-shared';
import { registerValidators, UIValidationRenderer } from "./ui/forms/ui-validation";
import { BreezeValidator } from './breeze-validator';
import * as breeze from 'breeze-client';
import { EventAggregator } from 'aurelia-event-aggregator';
import { Backend, TCustomAttribute } from 'aurelia-i18n';
import moment from 'moment';
import { HttpClient, HttpClientConfiguration } from 'aurelia-fetch-client';
import { ValidationController } from 'aurelia-validation';
import { DateUtils } from './utils/date-utils';
import { UIChat } from './ui/chat/ui-chat';
import { UINotificationAlert } from './ui/chat/ui-notification-alert';
import { UISegmentedControl } from './ui/forms/ui-segmented-control';
import { UISegmentedOption } from './ui/forms/ui-segmented-control-option';
import { UIPagination } from './resources/elements/ui-pagination';
import { UITab } from './ui/tab-panel/ui-tab';
import { UITabbarStart } from './ui/tab-panel/ui-tab-start';
import { UITabbarEnd } from './ui/tab-panel/ui-tab-end';
import { UIInfiniteScrollCustomAttribute } from './attributes/ui-infinite-scroll';
import { Auth0Service, AuthService, DWAuthService } from './security/index';
import { UIThemeObserverAttribute } from './attributes/ui-theme-observer';
import { PredicateUtils } from './data/predicate-utils';
import { RemoteFileStorage } from './services/file-storage-service';
import { LanguageCode } from './constants/constants';

export enum UIResources {
  Buttons = "buttons"
  // Card = "card",
  // Panel = "panel",
  // Menus = "menus",
  // Forms = "forms",
  // Lists = "lists",
  // TabPanel = "tabpanel",
  // DataPanel = "datapanel",
  // Calendar = "calendar",
  // Gridder = "gridder"
}

// const RESOURCE_LOADER: Record<string, () => Promise<AnyObject>> = {
//   ["panel"]: () => import("../src/ui/panels/ui-panels").then(m => m.Panels)
// };

type httpClientConfigurationFn = (config: HttpClientConfiguration) => void;

export class UIFrameworkConfig {
  private resources = [
    "base-app",
    "config",
    "breeze-validator",
    "breeze-model",
    "data-context",
    "entity-manager",
    "repository"
  ];

  @computedFrom("auConfig")
  public get AuConfig(): FrameworkConfiguration {
    return this.auConfig;
  }

  DefaultLang: string = LanguageCode.FR;
  ApplicationConfigImplementation: Function = null;
  BreezeModelImplementation: Function = null;
  AuthServiceImplemententation: Function = null;
  FileStorageServiceImplemententation: Function = null;
  HttpClientConfiguration: httpClientConfigurationFn = null;

  constructor(private auConfig: FrameworkConfiguration, loadResources: (callback: () => Promise<void>) => void) {
    loadResources(() => this.loadResources());
  }
  public setDefaultLang(lang: string): UIFrameworkConfig {
    this.DefaultLang = lang;
    return this;
  }
  public setApplicationConfigImplementation(configModule: any): UIFrameworkConfig {
    this.ApplicationConfigImplementation = configModule;
    return this;
  }
  public setBreezeModelImplementation(breezeModelModule: any): UIFrameworkConfig {
    this.BreezeModelImplementation = breezeModelModule;
    return this;
  }
  public setAuthServiceImplementation(authserviceModule: any): UIFrameworkConfig {
    this.AuthServiceImplemententation = authserviceModule;
    return this;
  }
  public setFileStorageServiceImplementation(fileStorageService?: any): UIFrameworkConfig {
    if (fileStorageService != null) {
      this.FileStorageServiceImplemententation = fileStorageService;
    } else {
      this.FileStorageServiceImplemententation = RemoteFileStorage.DWService;
    }
    return this;
  }
  public setHttpClientConfiguration(configurationFn: httpClientConfigurationFn): UIFrameworkConfig {
    this.HttpClientConfiguration = configurationFn;
    return this;
  }
  public autoDetectAuthServiceImplementation(authConfig: AuthConfig) {
    let authserviceModule = null;
    if ((authConfig.originalDomain != null && authConfig.originalDomain.endsWith('.auth0.com'))
      || authConfig.domain.endsWith('.auth0.com')) {
      authserviceModule = Auth0Service;
    } else {
      authserviceModule = DWAuthService;
    }
    return this.setAuthServiceImplementation(authserviceModule);
  }
  checkPrerequisites() {
    if (this.ApplicationConfigImplementation == null) throw "You must provide an implementation of Config abstract class";
    if (this.AuthServiceImplemententation == null) throw "You must provide an implementation of AuthService abstract class";
    if (this.BreezeModelImplementation == null) throw "You must provide an implementation of BreezeModel abstract class";
  }

  // public setApiBaseUrl(v: string): UIFrameworkConfig {
  //   AppConfig.prototype.ApiBaseUrl = v;
  //   return this;
  // }

  // public setApiHeaders(v: KeyValue): UIFrameworkConfig {
  //   AppConfig.prototype.ApiHeaders = v;
  //   return this;
  // }

  // public setKeyValue(key: string, v: AnyObject): UIFrameworkConfig {
  //   AppConfig.prototype[key] = v;
  //   return this;
  // }

  public useStandardResources() {
    //   this.useResource(UIResources.Buttons);
    //   this.useResource(UIResources.Calendar);
    //   this.useResource(UIResources.Card);
    //   this.useResource(UIResources.DataPanel);
    //   this.useResource(UIResources.Forms);
    //   this.useResource(UIResources.Gridder);
    //   this.useResource(UIResources.Lists);
    //   this.useResource(UIResources.Menus);
    //   this.useResource(UIResources.Panel);
    //   this.useResource(UIResources.TabPanel);
  }

  public useResource(resource: UIResources) {
    this.resources.push(resource);
    return this;
  }

  private loadResources() {

    // return Promise.all(this.resources.map(name => RESOURCE_LOADER[name]())).then(modules => {
    //   this.auConfig.globalResources(modules);
    // });      
    var attributes = Object.keys(AttributeHelpers).map(k => {
      if (AttributeHelpers[k] instanceof Function)
        return AttributeHelpers[k];
      else
        return null;
    });

    this.auConfig.globalResources([
      PLATFORM.moduleName('./resources/converters/date-format-value-converter'),
      PLATFORM.moduleName('./resources/converters/get-value-converter'),
      PLATFORM.moduleName('./resources/converters/french-number-converter'),
      PLATFORM.moduleName('./resources/elements/custom-paging'),
      PLATFORM.moduleName('./resources/elements/custom-ag-grid'),
      PLATFORM.moduleName('./resources/elements/grid-header-bar'),
      PLATFORM.moduleName('./resources/elements/tiny-mce'),
      PLATFORM.moduleName('./resources/elements/flow-uploader'),
      PLATFORM.moduleName('./resources/elements/loading-indicator'),
      PLATFORM.moduleName('./resources/elements/previewers/file-previewer'),
      PLATFORM.moduleName('./resources/elements/summary-filters/summary-filter'),
      PLATFORM.moduleName('./resources/attributes/select2-attribute'),
      PLATFORM.moduleName('./resources/attributes/place-autocomplete'),
      PLATFORM.moduleName('./ag-grid/agGridAurelia'),
      PLATFORM.moduleName('./ag-grid/agGridColumn'),
      PLATFORM.moduleName('./ag-grid/agTemplate'),
      PLATFORM.moduleName('./ui/viewport/ui-viewport'),
      PLATFORM.moduleName('./ui/viewport/ui-router-view'),
      PLATFORM.moduleName('./ui/viewport/ui-tab-router-view/ui-tab-router-view'),
      PLATFORM.moduleName('./ui/viewport/ui-viewport-footer'),
      PLATFORM.moduleName('./ui/viewport/ui-viewport-header'),
      PLATFORM.moduleName('./ui/viewport/ui-viewport-leftpart'),
      PLATFORM.moduleName('./ui/menu/ui-appbar'),
      PLATFORM.moduleName('./ui/data/tree/tree-node'),
      PLATFORM.moduleName('./ui/data/tree/ui-tree-panel'),
      PLATFORM.moduleName('./ui/pictures/ui-lightbox'),
      PLATFORM.moduleName('./ui/calendar/ui-date-picker'),
      PLATFORM.moduleName('./ui/calendar/ui-range-picker'),
      PLATFORM.moduleName('./ui/calendar/ui-scheduler'),
      PLATFORM.moduleName('./ui/buttons/ui-button'),
      PLATFORM.moduleName('./ui/buttons/ui-button-group'),
      PLATFORM.moduleName('./ui/buttons/ui-tag'),
      PLATFORM.moduleName('./ui/shared/ui-drop'),
      PLATFORM.moduleName('./ui/data/ui-tag-select'),
      PLATFORM.moduleName('./ui/data/ui-tag-list-check'),
      PLATFORM.moduleName('./ui/data/grid/ui-column'),
      PLATFORM.moduleName('./ui/data/grid/ui-table'),
      PLATFORM.moduleName('./ui/data/grid/ui-table-action'),
      PLATFORM.moduleName('./ui/data/ui-table-pagination/ui-table-pagination'),
      PLATFORM.moduleName('./ui/data/ui-tag-check'),
      PLATFORM.moduleName('./ui/anchor-tab/ui-anchor-tab'),
      PLATFORM.moduleName('./ui/metadata/metadata-detail'),
      PLATFORM.moduleName('./modules/dialogs/floating-box-view.html'),
      PLATFORM.moduleName('./ui/page/ui-dialog-header'),
      PLATFORM.moduleName('./ui/tab-panel/ui-tab-panel'),
      PLATFORM.moduleName('./resources/elements/step-timeline/step-timeline'),
      PLATFORM.moduleName('./resources/global-loader/global-loader'),
      ...Panels,
      ...Page,
      ...Shared,
      ...MenuComponents,
      ...ValueConverters,
      UISvgIcon, UIIcon, UIFlag,
      ...Forms,
      ...attributes.filter(f => f != null),
      UITooltip,
      UIBadge,
      UIContextmenu,
      UIInputmaskCustomAttribute,
      UIEntityHistory,
      Avatar,
      UIChat,
      UINotification,
      UINotificationList,
      UINotificationAlert,
      UISegmentedOption,
      UISegmentedControl,
      UIPagination,
      ...UIFilesComponents,
      ...Menus,
      UITab,
      UITabbarStart,
      UITabbarEnd,
      UIInfiniteScrollCustomAttribute,
      UIAlert,
      UserBaseForm,
      UIThemeObserverAttribute
    ]);
    return Promise.resolve();
  }
}

export function configure(
  auConfig: FrameworkConfiguration, configCallback: (config: UIFrameworkConfig) => void) {
  Container.instance = auConfig.container;

  let loadResources = () => Promise.resolve();
  const config = new UIFrameworkConfig(auConfig, fn => {
    loadResources = fn;
  });
  if (isFunction(configCallback)) {
    configCallback(config);
  } else {
    config.useStandardResources();
  }

  configureDefaultPlugins(config);

  configureHttpClient(config.HttpClientConfiguration);
  config.checkPrerequisites();
  auConfig.singleton(Config, config.ApplicationConfigImplementation);
  auConfig.singleton(BreezeModel, config.BreezeModelImplementation);
  auConfig.singleton(AuthService, config.AuthServiceImplemententation);
  auConfig.singleton(RemoteFileStorage.Service, config.FileStorageServiceImplemententation);
  auConfig.singleton(CommunicationHubClient);
  auConfig.singleton(BreezeSyncHubClient);
  auConfig.singleton(SecurityHubClient);
  auConfig.singleton(DateUtils);
  auConfig.singleton(PredicateUtils);

  return loadResources();
}
export * from './exports';

enum TranslationAttribute {
  Lower = "lower",
  Proper = "proper",
  Upper = "upper",
  UpperFirst = "upperFirst",
  LowerFirst = "lowerFirst"
};

function configureDefaultPlugins(frameworkConfig: UIFrameworkConfig) {
  return frameworkConfig.AuConfig
    .plugin(PLATFORM.moduleName('aurelia-validation'), config => {
      config.customValidator(BreezeValidator);
    })
    .plugin(PLATFORM.moduleName('aurelia-breeze'), () => {
      breeze.config.initializeAdapterInstances({ dataService: 'webApi', uriBuilder: 'json' });
    })
    .plugin(PLATFORM.moduleName('aurelia-i18n'), (instance) => {
      let aliases = ['t'];
      // add aliases for 't' attribute
      TCustomAttribute.configureAliases(aliases);
      // register backend plugin
      instance.i18next.use(Backend.with(frameworkConfig.AuConfig.aurelia.loader));

      // register $t alternatives
      instance.i18next.use({
        type: 'postProcessor',
        name: 'handleReferences',
        process: (value: string, key: string, options: Partial<{ _refLoopDetection?: string[] }>, translator: any) => {
          // Detect loops
          options._refLoopDetection ??= [];
          if (options._refLoopDetection.find(x => x == key)) {
            // Loop detected
            console.warn(`Translation reference loop detected on key ${key}: ${options._refLoopDetection.join(', ')}`);
            return key;
          }
          options._refLoopDetection.push(key);

          const trAttrRegex: RegExp = /\$(?<attr>.+?)\((?<referencedKey>.+?)\)/g;
          let result = value?.replace(trAttrRegex, (match, attr, referencedKey) => {

            let tr = translator.translate(referencedKey, options);
            switch (attr) {
              case TranslationAttribute.Lower: return tr.toLowerCase();

              case TranslationAttribute.Proper:
                let words = tr.split(" ");
                for (let word of words) {
                  word = word.charAt(0).toUpperCase() + word.slice(1);
                }
                return words.join(" ");

              case TranslationAttribute.Upper: return tr.toUpperCase();

              case TranslationAttribute.UpperFirst: return tr.charAt(0).toUpperCase() + tr.slice(1);

              case TranslationAttribute.LowerFirst: return tr.charAt(0).toLowerCase() + tr.slice(1);

              default:
                console.warn(`Unknown attribute detected : \$${attr}(${referencedKey})`);
                return key;
            }
          });
          options._refLoopDetection.pop();
          return result;
        }
      });
      let options = instance.i18next.options;
      if (false == isArray(options.postProcess)) {
        if (isString(options.postProcess)) {
          options.postProcess = [options.postProcess];
        } else {
          options.postProcess = []
        }
      }
      options.postProcess.push('handleReferences');

      // adapt options to your needs (see http://i18next.com/docs/options/)
      // make sure to return the promise of the setup method, in order to guarantee proper loading
      return instance.setup({
        backend: {                                  // <-- configure backend settings
          //loadPath: './src/locales/{{lng}}/{{ns}}.json', // <-- XHR settings for where to get the files from
          loadPath: (lngs, namespaces) => {
            if (namespaces.length > 0 && namespaces[0] === 'common') return 'digiwall-lib/dist/native-modules/locales/{{ns}}-{{lng}}.json';
            //if (namespaces.length > 0 && namespaces[0] === 'common') return 'digiwall-lib/locales/{{lng}}/common-translation.json';
            //if (namespaces.length > 0 && namespaces[0] === 'common') return 'resources/locales/{{ns}}-{{lng}}.json';
            return './locales/{{ns}}-{{lng}}.json';
          }
        },
        lng: frameworkConfig.DefaultLang,
        attributes: aliases,
        fallbackLng: 'fr',
        debug: false,
        ns: ['translation', 'common'],
        defaultNS: 'translation',
        fallbackNS: 'common'
      }).then(() => {
        const router = Container.instance.get(AppRouter);
        router.transformTitle = title => instance.tr(title);

        const eventAggregator = Container.instance.get(EventAggregator);
        eventAggregator.subscribe('i18n:locale:changed', () => {
          router.updateTitle();
        });
        moment.locale(frameworkConfig.DefaultLang);
      });
    })
    .plugin(PLATFORM.moduleName('aurelia-animator-css'))
    .plugin(PLATFORM.moduleName('aurelia-image-resizer'))
    .plugin(PLATFORM.moduleName('aurelia-ui-virtualization'))
    .postTask(() => {
      let val = Container.instance.get(ValidationController);
      val.addRenderer(new UIValidationRenderer());
      // Container.instance.registerHandler("ui-validator", container =>
      //   container.get(UIValidationRenderer)
      // );    
      registerValidators(Container.instance);

    });
}

function configureHttpClient(configFn?: httpClientConfigurationFn) {
  let httpClient = Container.instance.get(HttpClient);
  httpClient.configure(config => {
    config
      .withDefaults({
        credentials: 'same-origin'
      });
  });
  if (configFn != null) {
    httpClient.configure(configFn);
  }
}