import {
  customElement,
  html,
  type TemplateResult,
  DeesElement,
  type CSSResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';

import { DeesInputCheckbox } from './dees-input-checkbox.js';
import { DeesInputText } from './dees-input-text.js';
import { DeesInputQuantitySelector } from './dees-input-quantityselector.js';
import { DeesInputRadio } from './dees-input-radio.js';
import { DeesFormSubmit } from './dees-form-submit.js';
import { DeesTable } from './dees-table.js';
import { demoFunc } from './dees-form.demo.js';
import { DeesInputIban } from './dees-input-iban.js';

// Unified set for form input types
const FORM_INPUT_TYPES = [
  DeesInputCheckbox,
  DeesInputIban,
  DeesInputText,
  DeesInputQuantitySelector,
  DeesInputRadio,
  DeesTable,
];

export type TFormInputElement =
  | DeesInputCheckbox
  | DeesInputIban
  | DeesInputText
  | DeesInputQuantitySelector
  | DeesInputRadio
  | DeesTable<any>;

declare global {
  interface HTMLElementTagNameMap {
    'dees-form': DeesForm;
  }
}

@customElement('dees-form')
export class DeesForm extends DeesElement {
  public static demo = demoFunc;

  public name: string = 'myform';
  public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
  public readyDeferred = domtools.plugins.smartpromise.defer();

  public render(): TemplateResult {
    return html`
      <style>
        :host {
          display: contents;
        }
      </style>
      <slot></slot>
    `;
  }

  public async firstUpdated() {
    const formChildren = this.getFormElements();
    this.updateRequiredStatus();

    for (const child of formChildren) {
      child.changeSubject.subscribe(async () => {
        const valueObject = await this.collectFormData();
        this.changeSubject.next(valueObject);
        console.log(valueObject);
        this.updateRequiredStatus();
      });
    }
    await this.addBehaviours();
    this.readyDeferred.resolve();
  }

  public getFormElements(): Array<TFormInputElement> {
    return Array.from(this.children).filter((child) =>
      FORM_INPUT_TYPES.includes(child.constructor as any)
    ) as unknown as TFormInputElement[];
  }

  public getSubmitButton(): DeesFormSubmit | undefined {
    return Array.from(this.children).find(
      (child) => child instanceof DeesFormSubmit
    ) as DeesFormSubmit;
  }

  public async updateRequiredStatus() {
    console.log('checking the required status.');

    let requiredOK = true;
    for (const childArg of this.getFormElements()) {
      if (childArg.required && !childArg.value) {
        requiredOK = false;
      }
    }
    if (this.getSubmitButton()) {
      this.getSubmitButton().disabled = !requiredOK;
    }
  }

  /**
   * collects the form data
   * @returns
   */
  public async collectFormData() {
    const children = this.getFormElements();
    const valueObject: { [key: string]: string | number | boolean | any[] } = {};
    for (const child of children) {
      if (!child.key) {
        console.log(`form element with label "${child.label}" has no key. skipping.`);
      }
      valueObject[child.key] = child.value;
    }
    return valueObject;
  }

  public async gatherAndDispatch() {
    const valueObject = await this.collectFormData();
    const formDataEvent = new CustomEvent('formData', {
      detail: {
        data: valueObject,
      },
      bubbles: true,
    });
    this.dispatchEvent(formDataEvent);
    console.log('dispatched data:');
    console.log(valueObject);
  }

  public setStatus(
    visualStateArg: 'normal' | 'pending' | 'error' | 'success',
    textStateArg: string
  ) {
    const inputChildren = this.getFormElements();
    const submitButton = this.getSubmitButton();

    switch (visualStateArg) {
      case 'normal':
        submitButton.disabled = false;
        submitButton.status = 'normal';
        for (const inputChild of inputChildren) {
          inputChild.disabled = false;
        }
        break;
      case 'pending':
        submitButton.disabled = true;
        submitButton.status = 'pending';
        for (const inputChild of inputChildren) {
          inputChild.disabled = true;
        }
        break;
      case 'success':
        submitButton.disabled = true;
        submitButton.status = 'success';
        for (const inputChild of inputChildren) {
          inputChild.disabled = true;
        }
        break;
      case 'error':
        submitButton.disabled = true;
        submitButton.status = 'error';
        for (const inputChild of inputChildren) {
          inputChild.disabled = true;
        }
        break;
    }

    submitButton.text = textStateArg;
  }

  /**
   * resets the form
   */
  reset() {
    const inputChildren = this.getFormElements();
    const submitButton = this.getSubmitButton();

    for (const inputChild of inputChildren) {
      inputChild.value = null;
    }
    this.setStatus('normal', 'Submit');
  }

  public async addBehaviours() {
    // Use event delegation
    this.addEventListener('keydown', (event: KeyboardEvent) => {
      const target = event.target as DeesElement;
      if (!FORM_INPUT_TYPES.includes(target.constructor as any)) return;

      if (event.key === 'Enter') {
        const children = this.getFormElements();
        const currentIndex = children.indexOf(target as any);
        if (currentIndex < children.length - 1) {
          children[currentIndex + 1].focus();
        } else {
          target.blur();
          this.getSubmitButton()?.focus();
        }
      }
    });
  }
}