import { Schema } from '@devsun/eva-models';
import get from 'lodash/get';
import set from 'lodash/set';
import store from '@state/store';

export function createDictionaryMapFromSchema(
  schema: Partial<Schema.MetaClassSchemaRaw>
): DictionaryMap {
  return parseAttrList({}, schema);
}

export interface DictionaryMap {
  [key: string]: string;
}

export type Dictionary = Record<
  string,
  {
    value: string;
  }
>;

const SEPARATOR = '.';

function parseAttrList(
  resultingMap: DictionaryMap,
  schema: Partial<Schema.MetaClassSchemaRaw>,
  path = ''
): DictionaryMap {
  schema?.attrList?.forEach((attr) => {
    if (attr?.props?.dictionary) {
      resultingMap[`${path}${path ? SEPARATOR : ''}${attr.name}`] = attr.props
        .dictionary as string;
    }

    if (
      attr.reference?.service &&
      attr.reference?.entity &&
      schema.linkedSchema
    ) {
      resultingMap = parseAttrList(
        resultingMap,
        schema.linkedSchema[attr.reference.service][attr.reference.entity],
        `${path}${path ? SEPARATOR : ''}${attr.name}`
      );
    }
    if (attr.reference?.listAttrs) {
      resultingMap = parseAttrList(
        resultingMap,
        {
          attrList: attr.reference.listAttrs,
        } as Schema.MetaClassSchemaRaw,
        `${path}${path ? SEPARATOR : ''}${attr.name}`
      );
    }
  });

  return resultingMap;
}

export function swapModelValueWithDict<T>(
  path: string,
  model: T,
  dictionary: Dictionary
): T {
  const levels = path.split(SEPARATOR);

  // todo name conflicts if parent & child have same attr
  function setTreelike(start: number, path: Array<string | number> = []) {
    if (start === levels.length) {
      return;
    }

    const target = get(model, path);
    const targetKey = levels[start];
    const next = levels.filter((el, i) => i > start);

    if (next.length) {
      if (Array.isArray(target)) {
        target.forEach((el, i) => {
          setTreelike(start, [...path, i]);
        });
      } else {
        setTreelike(start + 1, [...path, targetKey]);
      }
    } else {
      if (Array.isArray(target)) {
        target.forEach((el, i) => {
          setTreelike(start, [...path, i]);
        });
      } else {
        const newTarget = target ? target : model;
        const value = get(newTarget, targetKey);
        if (dictionary.hasOwnProperty(value)) {
          set(newTarget, targetKey, dictionary[value].value);
          console.log(`✅ ${value} -> ${dictionary[value].value}`);
        } else {
          console.log(`⛔️ fail on ${value}`, dictionary);
          console.log({
            target,
            targetKey,
            levels,
          });
          console.dir(path);
        }
      }
    }
  }

  setTreelike(0);

  return model;
}

export async function setValuesFromDictionaries<T>(
  data: T,
  dictionaryMap: DictionaryMap
): Promise<T> {
  let result = { ...data };
  console.log(
    '[Dict-maps-utils][setValuesFromDictionaries] MAP',
    dictionaryMap
  );
  console.groupCollapsed(
    '[Dict-maps-utils][setValuesFromDictionaries] details:'
  );

  for (const path in dictionaryMap) {
    const dictCode = dictionaryMap[path];
    if (dictCode) {
      const dict = await store.dispatch(
        'registryService/referenceBooks/fetchReferenceBook',
        { id: dictCode }
      );
      result = swapModelValueWithDict(path, result, dict);
    }
  }
  console.groupEnd();

  return result;
}
