import { Prop, PropSync, Component, Watch, Vue } from 'vue-property-decorator';
import { GraphApi } from '@openapi/idm';

const graphApi = new GraphApi();

@Component({})
export default class BaseTreeSelector extends Vue {
  @PropSync('selectedLabel', { default: '' }) selectedLabelProp?:
    | string
    | undefined;

  @PropSync('selectedValue', { default: '' }) selectedValueProp: string;

  @Prop({ default: '' }) label?: string;
  @Prop({ default: 'fullname' }) treeLabel: string;
  @Prop({ default: '' }) currentRequestId: string;
  @Prop({ default: '' }) currentObjectId?: string | number;
  @Prop({ default: [] }) schemas: string[];
  @Prop({ default: 1000 }) depth?: number;
  @Prop({ default: '' }) schema: string;
  @Prop({ default: [] }) links: string[];
  @Prop({ default: null }) valuesToFilter: FilterValuesRequiredFields[] | null;

  private options: BasicTreeValue[] = []
  private optionsByFilter: BasicTreeValue[] = []
  private model: BasicTreeValue | null = null

  private filterFn(value: string, update: any):void {
    if (value === '') {
      update(() => {
        this.optionsByFilter = this.options
      })
      return
    }

    update(() => {
      const needle = value.toLowerCase()
      this.optionsByFilter = this.options.filter((v:BasicTreeValue) => {
        if (v.label) {
          return v.label.toLowerCase().indexOf(needle) > -1
        }
      })
    })
  }

  @Watch('model', { immediate: true, deep: true })
  recordsModel(val: BasicTreeValue | null) {
    this.selectedValueProp = val?.id || ''
    this.selectedLabelProp = val?.label || ''
  }

  @Watch('records', { immediate: true, deep: true })
  recordsWatcher() {
    this.options = []
    this.records.forEach((record: BasicTreeValue) => {
      if (record?.children?.length) {
        record.children.forEach((recordChild: BasicTreeValue) => {          
          this.options.push({
            ...recordChild,
            parent: record,
          })
        })
      } else {
        this.options.push(record)
      }
    })
  }

  @Watch('currentRequestId', { immediate: true })
  currentIdWatcher(val: string) {
    if (val) {
      this.initTree();
    }
  }

  private records: BasicTreeValue[] = [];

  private initTree(): void {
    graphApi
      .tree(
        this.schema,
        'incoming',
        this.currentRequestId,
        this.depth,
        this.schemas,
        this.links
      )
      .then(({ data }) => {
        const getTreeStructure = (node: BasicNodeValue) => {
          let isIdCheckFailed = false;
          let isChildCheckFailed = false;

          const idCheck = (val: FilterValuesRequiredFields) => {
            const res = val.id === node.value.id;
            if (res && !isIdCheckFailed) {
              isIdCheckFailed = true;
            }
            return res;
          };
          const childCheck = (val: FilterValuesRequiredFields) => {
            const res = node.leafs?.find((leaf) => leaf.value.id === val.id);
            if (res && !isChildCheckFailed) {
              isChildCheckFailed = true;
            }
            return res;
          };
          if (this.valuesToFilter) {
            if (
              !this.valuesToFilter.find(
                (val) => idCheck(val) || childCheck(val)
              )
            ) {
              return null;
            }
          }
          const result: BasicTreeValue = {
            disabled: this.currentObjectId === node.value?.id || false,
            selectable: !(!isIdCheckFailed && isChildCheckFailed),
            id: node.value.id,
            label:
              node.value[this.treeLabel] ||
              node.value.fullname ||
              node.value.name,
          };

          if (node.leafs && Array.isArray(node.leafs)) {
            result.children = node.leafs.reduce(
              (acc: BasicTreeValue[], item: BasicNodeValue) => {
                const filtered: BasicTreeValue | null = getTreeStructure(item);
                if (filtered) {
                  acc.push(filtered);
                }
                return acc;
              },
              []
            );
          }

          return result;
        };

        if (data.leafs && Array.isArray(data.leafs)) {
          // @ts-ignore
          // TODO: fix OPENAPI idm ts ? looks like a local mess
          this.records = data.leafs.reduce(
            // @ts-ignore
            // TODO: fix OPENAPI idm ts ? looks like a local mess
            (acc: BasicTreeValue[], item: BasicNodeValue) => {
              const filtered: BasicTreeValue | null = getTreeStructure(item);
              if (filtered) {
                acc.push(filtered);
              }
              return acc;
            },
            []
          );

          if (!this.records.length) {
            this.$emit('empty', true);
          }
        } else {
          this.records = [];
          this.$emit('empty', true);
        }
      })
      .catch(() => {
        throw new Error(
          '[components][common][base-tree-selector][index][graphApi]'
        );
      });
  }
}

declare interface FilterValuesRequiredFields {
  id: string;
}

declare interface BasicNodeValue {
  value: {
    id: string;
    [key: string]: string;
  };
  leafs?: BasicNodeValue[];
}

// todo - QTreeNode ?
declare interface BasicTreeValue {
  id: string;
  label?: string;
  disabled?: boolean;
  selectable?: boolean;
  expandable?: boolean;
  children?: BasicTreeValue[];
  hidden?: boolean;
  schema?: string;
  modified?: number;
  parent?:  BasicTreeValue;
}
