/**
 * Copyright Aquera Inc 2023
 *
 * This source code is licensed under the BSD-3-Clause license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { html, TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { styles } from './nile-switcher.css';
import NileElement from '../internal/nile-element';
import { CSSResultGroup } from 'lit';
import { choose } from 'lit/directives/choose.js';
import { classMap } from 'lit/directives/class-map.js';
import { repeat } from 'lit/directives/repeat.js';
import { watch } from '../internal/watch';
import { HasSlotController } from '../internal/slot';

/**
 * @summary Allows you to switch between nile elements
 *
 * @dependency nile-icon
 * @dependency nile-input
 * @dependency nile-checkbox
 * @dependency nile-dropdown
 * @dependency nile-textarea
 *
 * @event nile-change - Emitted when the control's value changes.
 * @event nile-switch - Emitted when the nile component is being switched.
 */

export interface switchconfig {
  [key: string]: any;
  toggleSwitch: boolean;
  disable?: boolean;
  confirmation?: boolean;
  align: 'block' | 'inline';
  inputs: switchInputType[];
}

export interface switchInputType {
  inputType:
    | INPUT_TYPE_NAMES.DROPDOWN
    | INPUT_TYPE_NAMES.TEXT
    | INPUT_TYPE_NAMES.CHECKBOX
    | INPUT_TYPE_NAMES.TEXTAREA
    | INPUT_TYPE_NAMES.RADIO
    | INPUT_TYPE_NAMES.CONTENTEDITOR
    | INPUT_TYPE_NAMES.OBJECT_MAPPER
    | INPUT_TYPE_NAMES.CODE_EDITOR;
  value?: String | boolean;
  label?: String;
  readonly?: boolean;
  type?: string;
  icon: string;
  mode?: string;
  placeholder?: string;
  disabled?: boolean;
  required?: boolean;
  error?: boolean;
  errorMessage?: string;
  options?: Array<any>;
  customAutoCompletions?: any;
  helperText?: string;
  multiple?: boolean;
  noborder?: boolean;
  multiLine?: boolean;
  expand?: boolean;
  updateValue?: false;
}

export enum POSITIONS {
  INLINE = 'inline',
  BLOCK = 'block',
}

export enum INPUT_TYPE {
  DEFAULT = 'defaultInput',
  SWITCH = 'switchInput',
}

export enum INPUT_TYPE_NAMES {
  DROPDOWN = 'dropdown',
  TEXT = 'text',
  CHECKBOX = 'checkbox',
  TEXTAREA = 'text-area',
  RADIO = 'radio',
  CONTENTEDITOR = 'content-editor',
  OBJECT_MAPPER = 'object-mapper',
  CODE_EDITOR = 'code-editor',
}

export enum MODE {
  CREATE = 'create',
  EDIT = 'edit',
}

@customElement('nile-switcher')
export class NileSwitcher extends NileElement {
  /**
   * The styles for nile switcher
   * @remarks If you are extending this class you can extend the base styles with super. Eg `return [super(), myCustomStyles]`
   */
  static styles: CSSResultGroup = styles;

  private readonly hasSlotController = new HasSlotController(this, 'label');

  @property({ type: Object }) nileSwitchConfig: switchconfig;

  @property({ type: Number }) current: Number = 0;

  @property({ type: Object }) currentInput: switchInputType;

  connectedCallback() {
    super.connectedCallback();
    this.emit('nile-init');
  }

  @watch(['current'], { waitUntilFirstUpdate: true })
  handleSwitcherChange() {
    this.setCurrentInput();
  }

  setCurrentInput() {
    if (this.nileSwitchConfig?.inputs) {
      this.currentInput = this.nileSwitchConfig.inputs.filter(
        (input: switchInputType, index: Number) => {
          return index === this.current;
        }
      )[0];
    }
  }
  disconnectedCallback() {
    super.disconnectedCallback();
    this.emit('nile-destroy');
  }

  renderNileText(Input: switchInputType) {
    const {
      value,
      label,
      placeholder,
      disabled,
      required,
      error,
      errorMessage,
      inputType,
      noborder,
    } = Input;
    return html`<nile-input
      class=${classMap({
        'switcher-input': true,
        'switcher-input--noborder': !!noborder,
      })}
      .value=${value}
      .label=${label}
      .disabled=${disabled}
      .required=${required}
      .errorMessage=${errorMessage}
      .placeholder=${placeholder}
      .error=${error}
      @nile-input=${(e: CustomEvent) => {
        this.handleChange(e, inputType);
      }}
    ></nile-input>`;
  }

  renderDropdown(Input: switchInputType) {
    const {
      options,
      multiple,
      placeholder,
      disabled,
      value,
      error,
      errorMessage,
      inputType,
      noborder,
    } = Input;

    return html`<nile-select
      part="dropdown"
      class=${classMap({
        'switcher-dropdown': true,
        'switcher-dropdown--noborder': !!noborder,
      })}
      .placeholder=${placeholder}
      .disabled="${disabled}"
      .multiple="${multiple}"
      .value="${value}"
      .error="${error}"
      .errorMessage="${errorMessage}"
      @nile-change=${(e: CustomEvent) => {
        this.handleChange(e, inputType);
      }}
    >
      ${options?.map((option: any) => {
        return html`<nile-option .value="${option}">${option} </nile-option>`;
      })}
    </nile-select>`;
  }

  renderNileTextArea(Input: switchInputType) {
    const { value, disabled, readonly, errorMessage, error, inputType } = Input;

    return html`<nile-textarea
      class="switcher-textarea"
      .value=${value}
      .disabled=${disabled}
      ?readonly=${readonly}
      .error=${error}
      .errorMessage=${errorMessage}
      @nile-input=${(e: CustomEvent) => {
        this.handleChange(e, inputType);
      }}
    ></nile-textarea>`;
  }

  renderNileCheckBox(Input: switchInputType) {
    const { value, label, disabled, inputType } = Input;

    return html`<nile-checkbox
        class="switcher-checkbox"
        .checked=${value}
        .label=${label}
        .disabled=${disabled}
        @valueChange=${(e: CustomEvent) => {
          this.handleChange(e, inputType);
        }}
      ></nile-checkbox
      >; `;
  }

  renderNileRadio(Input: switchInputType) {
    const { options, value, disabled, errorMessage, inputType } = Input;

    const haserrorMessage = !!errorMessage;

    return html`<nile-radio-group
      class="switcher-radio-group"
      .value=${value}
      .disabled=${disabled}
      @change=${(e: CustomEvent) => {
        this.handleChange(e, inputType);
      }}
    >
      ${options &&
      options.map((option: any) => {
        return html`<nile-radio .value="${option}">${option} </nile-radio>`;
      })}
      ${haserrorMessage ? this.renderErrorMessage(errorMessage) : ''}
    </nile-radio-group> `;
  }

  renderErrorMessage(errorMessage: string) {
    return html`
      <nile-form-error-message>${errorMessage}</nile-form-error-message>
    `;
  }

  renderContentEditor(Input: switchInputType) {
    let {
      options,
      inputType,
      errorMessage,
      type,
      readonly,
      noborder,
      value,
      updateValue,
    } = Input;
    return html`<nile-content-editor
      class="switcher-content-editor"
      .value=${value}
      .options=${options}
      .type=${type}
      .readonly=${readonly}
      .noborder=${noborder}
      .updateValue=${updateValue}
      .errorMessage=${errorMessage}
      @nile-change=${(e: CustomEvent) => {
        this.handleChange(e, inputType);
      }}
    >
    </nile-content-editor>`;
  }

  renderObjectMapper(Input: switchInputType) {
    let { options, inputType, error, errorMessage, type, readonly, noborder } =
      Input;

    const value =
      this.currentInput.mode === MODE.CREATE
        ? 'Click to Create - Not Mapped'
        : 'Click to Edit';

    return html`<nile-input
      class=${classMap({
        'switcher-object-mapper': true,
        'switcher-object-mapper--noborder': !!noborder,
      })}
      .value=${value}
      .errorMessage=${errorMessage}
      .error=${error}
      readonly="true"
      @click="${(e: CustomEvent) =>
        this.handleChange(e, INPUT_TYPE_NAMES.OBJECT_MAPPER)}"
    >
      <nile-icon
        class="pointer-cursor switcher-object-mapper-icon"
        slot="suffix"
        name="var(--nile-icon-expand-2, var(--ng-icon-expand-06))"
        method="var(--nile-svg-method-fill, var(--ng-svg-method-stroke))"
        color="var(--nile-colors-primary-600)"
        size="16"
      >
      </nile-icon>
    </nile-input>`;
  }

  renderCodeEditor(Input: switchInputType) {
    const {
      value,
      multiLine,
      customAutoCompletions,
      disabled,
      readonly,
      errorMessage,
      error,
      inputType,
      noborder,
      expand,
      updateValue,
    } = Input;
    return html`<nile-code-editor
      part="switcher-code-editor"
      exportparts="code-editor-base"
      class=${classMap({
        'code-editor': true,
      })}
      @nile-expand="${(e: CustomEvent) =>
        this.handleExpand(e, INPUT_TYPE_NAMES.CODE_EDITOR)}"
      @nile-change="${(e: CustomEvent) =>
        this.handleChange(e, INPUT_TYPE_NAMES.CODE_EDITOR)}"
      .multiline="${multiLine}"
      .value="${value}"
      .readonly=${readonly}
      .expandable="${expand}"
      .error=${error}
      .noborder=${noborder}
      .updateValue=${updateValue}
      .errorMessage=${errorMessage}
      .customAutoCompletions="${customAutoCompletions}"
    ></nile-code-editor> `;
  }

  handleExpand(event: CustomEvent, inputType: string) {
    this.currentInput.expand = event.detail.expand;
    this.emit('nile-change', { input: this.currentInput });
  }

  handleChange(event: CustomEvent, inputType: string) {
    switch (inputType) {
      case INPUT_TYPE_NAMES.OBJECT_MAPPER:
        this.currentInput.value = 'clicked';
        break;
      case INPUT_TYPE_NAMES.CHECKBOX:
        this.currentInput.value = event.detail.checked;
        break;
      case INPUT_TYPE_NAMES.CODE_EDITOR:
        this.currentInput.value = event.detail.value;
        break;
      default:
        this.currentInput.value = event.detail.value;
    }
    event.stopPropagation();
    this.emit('nile-change', { input: this.currentInput });
  }

  renderIcon() {
    const inputs = this.nileSwitchConfig.inputs;
    const toolTipPosition =
      this.nileSwitchConfig.align === POSITIONS.BLOCK ? 'top' : 'bottom';

    return html`
      <div part="icons-container" class="switcher-icons-container">
        ${repeat(
          inputs,
          (input: any, index: number) => html` <nile-tooltip
            class=${classMap({
              'switcher-tooltip-container': true,
            })}
            content=${input.helperText}
            placement=${toolTipPosition}
          >
            <div  class=${classMap({
              'switcher-icon-container': true,
              current: index === this.current,
            })}>
               <nile-icon
              size="16"
              class=${classMap({
                'pointer-cursor': true,
                'pointer-cursor-not-allowed': index === this.current,
              })}
              .name=${input.icon}
              method="var(--nile-svg-method-fill, var(--ng-svg-method-stroke))"
              color="#000000"
              @click=${() => this.toggleField(input, index)}
            ></nile-icon>
            <div>
           
          </nile-tooltip>`
        )}
      </div>
    `;
  }

  isToggleSwitch = (item: any) => {
    return typeof item === 'undefined' || item;
  };

  toggleField(currentInput: switchInputType, index: Number) {
    this.emit('nile-switch', {
      inputClicked: index,
      currentInput: currentInput,
    });
  }

  singleFieldSwitcher() {
    this.setCurrentInput();

    return html`
      ${choose(
        this.currentInput.inputType,
        [
          [INPUT_TYPE_NAMES.TEXT, () => this.renderNileText(this.currentInput)],
          [
            INPUT_TYPE_NAMES.CHECKBOX,
            () => this.renderNileCheckBox(this.currentInput),
          ],
          [
            INPUT_TYPE_NAMES.TEXTAREA,
            () => this.renderNileTextArea(this.currentInput),
          ],
          [
            INPUT_TYPE_NAMES.RADIO,
            () => this.renderNileRadio(this.currentInput),
          ],
          [
            INPUT_TYPE_NAMES.DROPDOWN,
            () => this.renderDropdown(this.currentInput),
          ],
          [
            INPUT_TYPE_NAMES.CONTENTEDITOR,
            () => this.renderContentEditor(this.currentInput),
          ],
          [
            INPUT_TYPE_NAMES.OBJECT_MAPPER,
            () => this.renderObjectMapper(this.currentInput),
          ],
          [
            INPUT_TYPE_NAMES.CODE_EDITOR,
            () => this.renderCodeEditor(this.currentInput),
          ],
        ],
        () => this.renderNileText(this.currentInput)
      )}
    `;
  }

  public render(): TemplateResult {
    const hasLabelSlot = this.hasSlotController.test('label');
    const align = this.nileSwitchConfig.align;
    const disable = this.nileSwitchConfig.disable;
    return html`<div
      part="base"
      class=${classMap({
        'switcher-block': align === POSITIONS.BLOCK,
        'switcher-inline': align === POSITIONS.INLINE,
        'switcher-container': true,
      })}
    >
      <div class="label-container">
        ${hasLabelSlot
          ? html`
           <label
          part="switcher-label"
          class="switcher__label"
          for="input"
        >
          <slot name="label"></slot>
        </label>
          </div> `
          : ''}
        ${!disable ? html`${this.renderIcon()}` : ''}
      </div>
      ${this.singleFieldSwitcher()}
    </div>`;
  }
}

export default NileSwitcher;

declare global {
  interface HTMLElementTagNameMap {
    'nile-switcher': NileSwitcher;
  }
}
