/*
 * https://github.com/morethanwords/tweb
 * Copyright (C) 2019-2021 Eduard Kuzmenko
 * https://github.com/morethanwords/tweb/blob/master/LICENSE
 */

import cancelEvent from '../helpers/dom/cancelEvent';
import {attachClickEvent} from '../helpers/dom/clickEvent';
import findUpAsChild from '../helpers/dom/findUpAsChild';
import ListenerSetter from '../helpers/listenerSetter';
import safeAssign from '../helpers/object/safeAssign';
import I18n, {LangPackKey} from '../lib/langPack';
import CheckboxField from './checkboxField';
import Row from './row';
import {toast} from './toast';

export type CheckboxFieldsField = {
  text: LangPackKey,
  description?: LangPackKey,
  restrictionText?: LangPackKey,
  checkboxField?: CheckboxField,
  checked?: boolean,
  nested?: CheckboxFieldsField[],
  nestedTo?: CheckboxFieldsField,
  nestedCounter?: HTMLElement,
  setNestedCounter?: (count: number) => void,
  toggleWith?: {checked?: CheckboxFieldsField[], unchecked?: CheckboxFieldsField[]},
  name?: string,
  row?: Row
};

export default class CheckboxFields<K extends CheckboxFieldsField = CheckboxFieldsField> {
  public fields: Array<K>;
  protected listenerSetter: ListenerSetter;
  protected asRestrictions: boolean;

  constructor(options: {
    fields: Array<K>,
    listenerSetter: ListenerSetter,
    asRestrictions?: boolean
  }) {
    safeAssign(this, options);
  }

  public createField(info: CheckboxFieldsField, isNested?: boolean) {
    if(info.nestedTo && !isNested) {
      return;
    }

    const row = info.row = new Row({
      titleLangKey: isNested ? undefined : info.text,
      checkboxField: info.checkboxField = new CheckboxField({
        text: isNested ? info.text : undefined,
        checked: info.nested ? false : info.checked,
        toggle: !isNested,
        listenerSetter: this.listenerSetter,
        restriction: this.asRestrictions && !isNested,
        name: info.name
      }),
      listenerSetter: this.listenerSetter,
      subtitleLangKey: info.description,
      clickable: info.nested ? (e) => {
        if(findUpAsChild(e.target as HTMLElement, row.checkboxField.label)) {
          return;
        }

        cancelEvent(e);
        row.container.classList.toggle('accordion-toggler-expanded');
        accordion.classList.toggle('is-expanded');
      } : undefined
    });

    if(info.restrictionText) {
      info.checkboxField.input.disabled = true;

      attachClickEvent(info.checkboxField.label, (e) => {
        toast(I18n.format(info.restrictionText, true));
      }, {listenerSetter: this.listenerSetter});
    }

    const nodes: HTMLElement[] = [row.container];
    let accordion: HTMLElement, nestedCounter: HTMLElement;
    if(info.nested) {
      const container = accordion = document.createElement('div');
      container.classList.add('accordion');
      container.style.setProperty('--max-height', info.nested.length * 48 + 'px');
      const _info = info;
      info.nested.forEach((info) => {
        info.nestedTo ??= _info;
        container.append(...this.createField(info, true).nodes);
      });
      nodes.push(container);

      const span = document.createElement('span');
      span.classList.add('tgico-down', 'accordion-icon');

      nestedCounter = info.nestedCounter = document.createElement('b');
      this.setNestedCounter(info);
      row.title.append(' ', nestedCounter, ' ', span);

      row.container.classList.add('accordion-toggler');
      row.titleRow.classList.add('with-delimiter');

      row.checkboxField.setValueSilently(this.getNestedCheckedLength(info) === info.nested.length);

      info.toggleWith ??= {checked: info.nested, unchecked: info.nested};
    }

    if(info.toggleWith || info.nestedTo) {
      const processToggleWith = info.toggleWith ? (info: CheckboxFieldsField) => {
        const {toggleWith, nested} = info;
        const value = info.checkboxField.checked;
        const arr = value ? toggleWith.checked : toggleWith.unchecked;
        if(!arr) {
          return;
        }

        const other = this.fields.filter((i) => arr.includes(i));
        other.forEach((info) => {
          info.checkboxField.setValueSilently(value);
          if(info.nestedTo && !nested) {
            this.setNestedCounter(info.nestedTo);
          }

          if(info.toggleWith) {
            processToggleWith(info);
          }
        });

        if(info.nested) {
          this.setNestedCounter(info);
        }
      } : undefined;

      const processNestedTo = info.nestedTo ? () => {
        const length = this.getNestedCheckedLength(info.nestedTo);
        info.nestedTo.checkboxField.setValueSilently(length === info.nestedTo.nested.length);
        this.setNestedCounter(info.nestedTo, length);
      } : undefined;

      this.listenerSetter.add(info.checkboxField.input)('change', () => {
        processToggleWith?.(info);
        processNestedTo?.();
      });
    }

    return {row, nodes};
  }

  protected getNestedCheckedLength(info: CheckboxFieldsField) {
    return info.nested.reduce((acc, v) => acc + +v.checkboxField.checked, 0);
  }

  public setNestedCounter(info: CheckboxFieldsField, count = this.getNestedCheckedLength(info)) {
    info.nestedCounter.textContent = `${count}/${info.nested.length}`;
  }
}
