import sha256 from 'sha256';
import { ajvValidator } from '@/plugins/validator/ajv';
import { userSetting } from '@/plugins/setting';
import { KV_CRYPT } from '@/config/env';
import { loggerPlugin } from '@/plugins/logger';
import { KvAdapter } from './adapter';

const isShowLog = false;

const getUserId = () => userSetting.userId.get();
/**
 * @param {KvConfig} kvConfig
 */
export const initKvStore = (kvConfig) => {
  const {
    rootKey, timeExpired, schema, isGlobal, isEncrypt, selfCrypt,
  } = kvConfig;

  if (!rootKey) {
    throw new Error('KV_MISSING_ROOT_KEY');
  }

  if (schema) {
    ajvValidator.compile(schema);
  }

  class KvStoreApi {
    /**
     * @param {string} kvKey
     * @param {string} rootKey
     * @param {boolean} isGlobal
     * @returns {string}
     */
    static generateKvKey(kvKey, rootKey, isGlobal) {
      const userIdKv = getUserId();

      return ['kv', 'autoshopee-kit', rootKey, isGlobal ? 'global' : userIdKv, sha256(kvKey)].join(':');
    }

    /**
     * @param {string} rootKey
     * @param {boolean} selfCrypt
     * @param {string} kvKey
     * @returns {string}
     */
    static generateCryptKey(rootKey, selfCrypt, kvKey) {
      const keyParts = [rootKey, KV_CRYPT, selfCrypt ? kvKey : ''].filter((key) => key);

      return sha256(keyParts.join(':'));
    }

    /**
     * @param {string} kvKey
     */
    constructor(kvKey) {
      if (!kvKey) {
        throw new Error('KV_KEY_REQUIRED');
      }

      if (typeof kvKey !== 'string') {
        kvKey = JSON.stringify(kvKey);
      }

      this.originKey = [rootKey, kvKey].join('-');
      this.kvKey = KvStoreApi.generateKvKey(kvKey, rootKey, isGlobal);
      this.kvValue = null;
      this.ttl = timeExpired || 0;
      this.isExist = false;
      this.cryptKey = isEncrypt || selfCrypt ? KvStoreApi.generateCryptKey(rootKey, selfCrypt, kvKey) : '';
    }

    /**
     * @param {any} value
     * @returns {any}
     */
    validator(value) {
      if (!schema) {
        return value;
      }

      const validator = ajvValidator.compile(schema);
      const isValid = validator(value);

      if (!isValid) {
        throw new Error(ajvValidator.errorsText(validator.errors));
      }

      return value;
    }

    setTtl(ttl = 0) {
      this.ttl = ttl;
    }

    async get() {
      const getValue = await KvAdapter.get(this.kvKey, this.cryptKey);

      if (isShowLog) {
        loggerPlugin.info({
          msg: 'KV_API_FETCH',
          originKey: this.originKey,
          kvKey: this.kvKey,
          getValue,
        });
      }

      if (!getValue) {
        this.isExist = false;
        return null;
      }

      this.kvValue = getValue;

      try {
        this.kvValue = JSON.parse(this.kvValue);
      } catch (error) {}

      this.kvValue = this.validator(this.kvValue);

      if (isShowLog) {
        loggerPlugin.info({
          msg: 'KV_API_GET',
          originKey: this.originKey,
          kvKey: this.kvKey,
          kvValue: this.kvValue,
          isExist: this.isExist,
          getValue,
        });
      }

      return this.kvValue;
    }

    /**
     * @param setValue
     * @param {number} ttl
     * @returns {object}
     */
    async set(setValue, ttl = 0) {
      this.kvValue = setValue;
      this.isExist = true;

      const setTTl = ttl || this.ttl || 0;
      await KvAdapter.set(this.kvKey, setValue, setTTl, this.cryptKey);

      if (isShowLog) {
        loggerPlugin.info({
          msg: 'KV_API_SET',
          originKey: this.originKey,
          kvKey: this.kvKey,
          setValue,
        });
      }

      return {
        key: this.kvKey,
        value: setValue,
      };
    }

    async remove() {
      await KvAdapter.del(this.kvKey);
      this.isExist = false;

      return true;
    }

    getKv = this.get;

    setKv = this.set;

    removeKv = this.remove;
  }

  return KvStoreApi;
};
