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

import { DeesInputCheckbox } from '../../00group-input/dees-input-checkbox/dees-input-checkbox.js';
import { DeesInputCode } from '../../00group-input/dees-input-code/dees-input-code.js';
import { DeesInputDatepicker } from '../../00group-input/dees-input-datepicker/index.js';
import { DeesInputText } from '../../00group-input/dees-input-text/dees-input-text.js';
import { DeesInputQuantitySelector } from '../../00group-input/dees-input-quantityselector/dees-input-quantityselector.js';
import { DeesInputRadiogroup } from '../../00group-input/dees-input-radiogroup/dees-input-radiogroup.js';
import { DeesInputDropdown } from '../../00group-input/dees-input-dropdown/dees-input-dropdown.js';
import { DeesInputFileupload } from '../../00group-input/dees-input-fileupload/index.js';
import { DeesInputIban } from '../../00group-input/dees-input-iban/dees-input-iban.js';
import { DeesInputMultitoggle } from '../../00group-input/dees-input-multitoggle/dees-input-multitoggle.js';
import { DeesInputPhone } from '../../00group-input/dees-input-phone/dees-input-phone.js';
import { DeesInputToggle } from '../../00group-input/dees-input-toggle/dees-input-toggle.js';
import { DeesInputTypelist } from '../../00group-input/dees-input-typelist/dees-input-typelist.js';
import { DeesFormSubmit } from '../dees-form-submit/dees-form-submit.js';
import { DeesTable } from '../../dees-table/index.js';
import { demoFunc } from './dees-form.demo.js';

// Unified set for form input types
const FORM_INPUT_TYPES = [
  DeesInputCheckbox,
  DeesInputCode,
  DeesInputDatepicker,
  DeesInputDropdown,
  DeesInputFileupload,
  DeesInputIban,
  DeesInputMultitoggle,
  DeesInputPhone,
  DeesInputQuantitySelector,
  DeesInputRadiogroup,
  DeesInputText,
  DeesInputToggle,
  DeesInputTypelist,
  DeesTable,
];

export type TFormInputElement =
  | DeesInputCheckbox
  | DeesInputCode
  | DeesInputDatepicker
  | DeesInputDropdown
  | DeesInputFileupload
  | DeesInputIban
  | DeesInputMultitoggle
  | DeesInputPhone
  | DeesInputQuantitySelector
  | DeesInputRadiogroup
  | DeesInputText
  | DeesInputToggle
  | DeesInputTypelist
  | DeesTable<any>;

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

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

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

  /**
   * Controls the layout mode of child input components
   * When true, sets all child inputs to horizontal layout
   */
  @property({ type: Boolean, reflect: true, attribute: 'horizontal-layout' })
  accessor horizontalLayout: boolean = false;

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

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

    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[] | File[] | { option: string; key: string; payload?: any } } = {};
    
    for (const child of children) {
      if (!child.key) {
        console.log(`form element with label "${child.label}" has no key. skipping.`);
        continue;
      }
      
      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();
        }
      }
    });
  }

  /**
   * Updates the layout mode of child input components based on form's horizontalLayout property
   */
  private updateChildrenLayoutMode() {
    const formChildren = this.getFormElements();
    for (const child of formChildren) {
      if ('layoutMode' in child) {
        // The child's auto mode will detect this form's horizontal-layout attribute
        (child as any).layoutMode = 'auto';
      }
    }
  }

  /**
   * Called when properties change
   */
  updated(changedProperties: Map<string, any>) {
    super.updated(changedProperties);
    
    if (changedProperties.has('horizontalLayout')) {
      this.updateChildrenLayoutMode();
    }
  }
}
