import {
  applyUpdateChanges,
  PREFERENCES_VARS,
  preferences_vars_default,
} from '@caj/configs/appVar';
import {AppVarMaxBackups, APP_VERSION} from '@caj/configs/appNonSaveVar';
import {appVarActions} from '@caj/configs/appVarReducer';
import {store} from '@caj/redux/store';
import {getData, setData} from './storage/appData';
import {getVersionByType, stepUpVersionCalc} from './version';

const APP_CHANGE_BACKUP = 'appVerChangeBackup';

function setAppVar(appVar: PREFERENCES_VARS) {
  store.dispatch(appVarActions.loadPreferences(appVar));
}

function makeBackup(key: string, version: number): Promise<void> {
  return new Promise<void>(function (resolve, reject) {
    getData('appVar').then(value => {
      if (value !== null) {
        setData(key + '#' + version, value).then(() => {
          resolve();
        });
      } else {
        resolve();
      }
    });
  });
}

function refreshVersion(from: number, to: number): Promise<void> {
  return new Promise<void>(function (resolve, reject) {
    if (from < to) {
      // app upgrade
      console.log('app upgrade detected!');
      finish();
    } else if (from > to) {
      // app downgrade
      console.log('app downgrade detected!');
      finish();
    } else {
      // app version not changed
      finish();
    }

    function finish() {
      setData('appVersion', APP_VERSION.toString()).then(() => {
        resolve();
      });
    }
  });
}

export function initAppData(): Promise<void> {
  return new Promise<void>(function (resolve, reject) {
    let appVer: number = -1;

    getData('appVersion').then(_ver => {
      appVer = typeof _ver === 'string' ? Number.parseInt(_ver) : -1;

      if (appVer === -1) {
        // no version found in storage
        setData('appVersion', APP_VERSION.toString()).then(() => {
          appVer = APP_VERSION;
          preferencesPull();
        });

        return;
      } else if (appVer < APP_VERSION) {
        // found outdated version: make backup; and upgrade
        makeBackup(APP_CHANGE_BACKUP, appVer).then(() => {
          refreshVersion(appVer, APP_VERSION).then(() => {
            preferencesPull();
          });
        });
      } else if (appVer > APP_VERSION) {
        // version is too high: make backup; and "downgrade"
        makeBackup(APP_CHANGE_BACKUP, appVer).then(() => {
          refreshVersion(appVer, APP_VERSION).then(() => {
            preferencesPull();
          });
        });
      }
      preferencesPull();
    });

    function resetPreferences() {
      let appVar = preferences_vars_default;

      setData('appVar', JSON.stringify(appVar)).then(() => {
        setAppVar(appVar);
        resolve();
      });
    }

    function tryToApplyPreferences(
      appVar: PREFERENCES_VARS,
      didUpdate = false,
    ) {
      if (appVar.version < APP_VERSION) {
        {
          console.log('ver', appVar.version);
          console.log('config update needed until', APP_VERSION, appVar);
          applyUpdateChanges(appVar).then(() => {
            tryToApplyPreferences(appVar, true);
          });
          return;
        }
      }

      const changed = checkForUndefined(preferences_vars_default, appVar);

      if (didUpdate === true || changed === true) {
        setData('appVar', JSON.stringify(appVar)).then(() => {
          setAppVar(appVar);
          resolve();
        });
      } else {
        setAppVar(appVar);
        resolve();
      }
    }

    function preferencesPull() {
      getData('appVar').then(value => {
        if (value === null) {
          resetPreferences();
        } else {
          let appVarRaw = null;
          let isNoJSON = false;
          try {
            appVarRaw = JSON.parse(value);
          } catch (error) {
            console.error('corrupt appVar! :(');
            isNoJSON = true;
          }

          if (isNoJSON) {
            getData('appVarBackupIndex').then(_index => {
              let index: number =
                typeof _index === 'string'
                  ? Number.parseInt(_index)
                  : AppVarMaxBackups;
              index--;
              if (index < 0) index = AppVarMaxBackups - 1;

              setData('appVarBackup' + index, value).then(() => {
                setData('appVarBackupIndex', index.toString()).then(() => {
                  resetPreferences();
                });
              });
            });
          } else {
            let appVar: PREFERENCES_VARS = appVarRaw;

            tryToApplyPreferences(appVar);
          }
        }
      });
    }
  });
}

export function saveVarChanges() {
  let appVar = store.getState().appVariables.preferences;
  setData('appVar', JSON.stringify(appVar)).then(() => {
    console.log('saved!');
  });
}

export function checkForUndefined(objDef: any, objReal: any): boolean {
  let changed = false;

  function checkObj(objDef: any, objReal: any) {
    for (const key in objDef) {
      const defValue = objDef[key];
      let realValue = objReal[key];

      if (typeof defValue === 'undefined') continue;

      if (typeof realValue === 'undefined' || realValue === null) {
        objReal[key] = defValue;

        changed = true;
        console.log(key);
      } else if (
        typeof defValue === 'object' &&
        typeof realValue === 'object'
      ) {
        checkObj(defValue, realValue);
      }
    }
  }

  checkObj(objDef, objReal);
  return changed;
}
