import { PlatformBindings } from './PlatformBindings.js';
import { EventTypes } from '../../events/EventTypes.js';
import { IConfigEnvironment, ITelemetryConfig, IEnvConfig } from '../../lib/config/Config.js';

export const KeyPrefix = 'api-client.config.';
export const EnvironmentsKey = `${KeyPrefix}environments`;
export const TelemetryKey = `${KeyPrefix}telemetry`;

export interface LocalConfiguration {
  /**
   * Reads a previously stored local value.
   * 
   * @param key The key under which the property was stored.
   * @returns The configuration value or undefined when not found.
   */
  get(key: string): Promise<unknown | undefined>;

  /**
   * Sets a config property that is permanently stored.
   * 
   * @param key The key under to store the value.
   * @param value The value to store. If this is not a primitive it will be serialized with `JSON.stringify()`.
   */
  set(key: string, value: unknown): Promise<void>;

  /**
   * Deletes previously stored local value.
   * 
   * @param key The key under which the property was stored.
   */
  delete(key: string): Promise<void>;
}

export interface SessionConfiguration {
  /**
   * Reads a previously stored session value.
   * 
   * @param key The key under which the property was stored.
   * @returns The configuration value or undefined when not found.
   */
  get(key: string): Promise<unknown | undefined>;

  /**
   * Sets a config property that is held in the storage only for the time of the current session.
   * 
   * @param key The key under to store the value.
   * @param value The value to store. If this is not a primitive it will be serialized with `JSON.stringify()`.
   */
  set(key: string, value: unknown): Promise<void>;

  /**
   * Deletes previously stored session value.
   * 
   * @param key The key under which the property was stored.
   */
  delete(key: string): Promise<void>;
}

export interface AddConfigEnvironmentInit {
  /**
   *  Whether to set this environment asd default. Default to `false`.
   */
  asDefault?: boolean
}

export interface EnvironmentConfiguration {
  /**
   * Adds a new environment configuration to the application permanent storage.
   * This is not synchronized between applications.
   * 
   * @param env The environment to add.
   * @param init Add configuration environment options.
   */
  add(env: IConfigEnvironment, init?: AddConfigEnvironmentInit): Promise<void>;

  /**
   * Updates an existing environment.
   * 
   * @param env The environment to update
   */
  update(env: IConfigEnvironment): Promise<void>;

  /**
   * Reads the definition of an environment.
   * 
   * @param id The key of the environment to read. When not set it reads the default environment.
   */
  read(id?: string): Promise<IConfigEnvironment>;

  /**
   * Removes an environment from the permanent store.
   * @param id The key of the environment to remove.
   */
  remove(id: string): Promise<void>;

  /**
   * Sets the environment as default.
   * 
   * @param id The key of the environment to set as default.
   */
  setDefault(id: string): Promise<void>;

  /**
   * Lists all environments in the store.
   */
  list(): Promise<IEnvConfig>;
}

export interface TelemetryConfiguration {
  /**
   * Reads the set telemetry configuration. If the configuration is missing a default value is returned.
   */
  read(): Promise<ITelemetryConfig>;

  /**
   * Sets the configuration for telemetry.
   *
   * @param config The configuration to set.
   */
  set(config: ITelemetryConfig): Promise<void>;
}

/**
 * The base class for application settings bindings.
 */
export abstract class ConfigurationBindings extends PlatformBindings {
  abstract local: LocalConfiguration;

  abstract session: SessionConfiguration;

  abstract telemetry: TelemetryConfiguration;

  abstract environment: EnvironmentConfiguration;

  async initialize(): Promise<void> {
    window.addEventListener(EventTypes.Config.Environment.add, this.addEnvironmentHandler.bind(this));
    window.addEventListener(EventTypes.Config.Environment.update, this.updateEnvironmentHandler.bind(this));
    window.addEventListener(EventTypes.Config.Environment.read, this.readEnvironmentHandler.bind(this));
    window.addEventListener(EventTypes.Config.Environment.delete, this.removeEnvironmentHandler.bind(this));
    window.addEventListener(EventTypes.Config.Environment.setDefault, this.setDefaultHandler.bind(this));
    window.addEventListener(EventTypes.Config.Environment.list, this.listEnvironmentsHandler.bind(this));

    window.addEventListener(EventTypes.Config.Session.get, this.getSessionHandler.bind(this));
    window.addEventListener(EventTypes.Config.Session.set, this.setSessionHandler.bind(this));
    window.addEventListener(EventTypes.Config.Session.delete, this.deleteSessionHandler.bind(this));

    window.addEventListener(EventTypes.Config.Local.get, this.getLocalHandler.bind(this));
    window.addEventListener(EventTypes.Config.Local.set, this.setLocalHandler.bind(this));
    window.addEventListener(EventTypes.Config.Local.delete, this.deleteLocalHandler.bind(this));

    window.addEventListener(EventTypes.Config.Telemetry.read, this.telemetryReadHandler.bind(this));
    window.addEventListener(EventTypes.Config.Telemetry.set, this.telemetrySetHandler.bind(this));
  }

  protected addEnvironmentHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.environment.add(e.detail.env, { asDefault: e.detail.asDefault });
  }

  protected updateEnvironmentHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.environment.update(e.detail.env);
  }

  protected readEnvironmentHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.environment.read(e.detail.id);
  }

  protected removeEnvironmentHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.environment.remove(e.detail.id);
  }

  protected setDefaultHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.environment.setDefault(e.detail.id);
  }

  protected listEnvironmentsHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.environment.list();
  }

  protected getSessionHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.session.get(e.detail.key);
  }

  protected setSessionHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.session.set(e.detail.key, e.detail.value);
  }

  protected deleteSessionHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.session.delete(e.detail.key);
  }

  protected getLocalHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.local.get(e.detail.key);
  }

  protected setLocalHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.local.set(e.detail.key, e.detail.value);
  }

  protected deleteLocalHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.local.delete(e.detail.key);
  }

  protected telemetryReadHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.telemetry.read();
  }

  protected telemetrySetHandler(input: Event): void {
    const e = input as CustomEvent;
    e.preventDefault();
    e.detail.result = this.telemetry.set(e.detail.config);
  }

  // /**
  //  * Adds a new environment configuration to the application permanent storage.
  //  * This is not synchronized between applications.
  //  * 
  //  * @param env The environment to add.
  //  * @param asDefault Whether to set this environment asd default. Default to `false`.
  //  */
  // abstract addEnvironment(env: IConfigEnvironment, asDefault?: boolean): Promise<void>;

  // /**
  //  * Updates an existing environment.
  //  * 
  //  * @param env The environment to update
  //  */
  // abstract updateEnvironment(env: IConfigEnvironment): Promise<void>;

  // /**
  //  * Reads the definition of an environment.
  //  * 
  //  * @param id The key of the environment to read. When not set it reads the default environment.
  //  */
  // abstract readEnvironment(id?: string): Promise<IConfigEnvironment>;

  // /**
  //  * Removes an environment from the permanent store.
  //  * @param id The key of the environment to remove.
  //  */
  // abstract removeEnvironment(id: string): Promise<void>;

  // /**
  //  * Sets the environment as default.
  //  * 
  //  * @param id The key of the environment to set as default.
  //  */
  // abstract setDefaultEnvironment(id: string): Promise<void>;

  // /**
  //  * Lists all environments in the store.
  //  */
  // abstract readEnvironments(): Promise<IEnvConfig>;

  // /**
  //  * Sets a config property that is held in the storage only for the time of the current session.
  //  * 
  //  * @param key The key under to store the value.
  //  * @param value The value to store. If this is not a primitive it will be serialized with `JSON.stringify()`.
  //  */
  // abstract setSessionProperty(key: string, value: unknown): Promise<void>;

  // /**
  //  * Reads a previously stored session value.
  //  * 
  //  * @param key The key under which the property was stored.
  //  */
  // abstract getSessionProperty(key: string): Promise<unknown | undefined>;

  // /**
  //  * Deletes previously stored session value.
  //  * 
  //  * @param key The key under which the property was stored.
  //  */
  // abstract deleteSessionProperty(key: string): Promise<void>;

  // /**
  //  * Reads the set telemetry configuration. If the configuration is missing a default value is returned.
  //  */
  // abstract telemetryRead(): Promise<ITelemetryConfig>;

  // /**
  //  * Sets the configuration for telemetry.
  //  * 
  //  * @param config The configuration to set.
  //  */
  // abstract telemetrySet(config: ITelemetryConfig): Promise<void>;

  // /**
  //  * Sets a config property that is permanently stored.
  //  * This is not the same as application configuration. This can be used to store specific configuration 
  //  * property for a single view.
  //  * 
  //  * @param key The key under to store the value.
  //  * @param value The value to store. If this is not a primitive it will be serialized with `JSON.stringify()`.
  //  */
  // abstract setLocalProperty(key: string, value: unknown): Promise<void>;

  // /**
  //  * Reads a previously stored local value.
  //  * 
  //  * @param key The key under which the property was stored.
  //  * @param globalKey If known, the key of the configuration property in the application 
  //  * global configuration. When set and the value in the local storage is not set, then
  //  * the config store reads the value from the application configuration. 
  //  */
  // abstract getLocalProperty(key: string, globalKey?: string): Promise<unknown | undefined>;

  // /**
  //  * Deletes previously stored local value.
  //  * 
  //  * @param key The key under which the property was stored.
  //  */
  // abstract deleteLocalProperty(key: string): Promise<void>;
}
