
const $lcamelCase = require('lodash/camelCase');
// const $lget = require('lodash/get');
// const $lset = require('lodash/set');

export default function (
  {
    name,
    query = {},
    // eslint-disable-next-line no-unused-vars
    parseQuery = (value, key) => value,
    params = {},
    hash = {},
    runWhen = true,
  } = {}) {
  const prefix = $lcamelCase(name);

  let mixin = {
    watch: {
      $route: {
        immediate: true,
        deep: true,
        handler(to) {
          if (this[`${prefix}RunWhenCust`]) { // console.log('$route');
            Object.keys(to.query).forEach(query_key => {
              if ((query_key in this) && (query_key in this[`${prefix}RouteQueryCust`]) && to.query[query_key] !== this[query_key]) {
                try {
                  this[query_key] = parseQuery(to.query[query_key], query_key);
                } catch (e) {
                  console.error(e);
                }
              }
            }, this);

            Object.keys(to.params).forEach(params_key => {
              if ((params_key in this) && (params_key in this[`${prefix}RouteParamsCust`]) && to.query[params_key] !== this[params_key]) {
                try {
                  this[params_key] = to.params[params_key];
                } catch (e) {
                  console.error(e);
                }
              }
            }, this);

            Object.keys(to.hash).forEach(hash_key => {
              if ((hash_key in this) && (hash_key in this[`${prefix}RouteHashCust`]) && to.query[hash_key] !== this[hash_key]) {
                try {
                  this[hash_key] = to.hash[hash_key];
                } catch (e) {
                  console.error(e);
                }
              }
            }, this);
          }
        },
      },
      [`${prefix}RouteQueryCust`]: {
        immediate: true,
        deep: true,
        handler(newVal) {
          if (Object.keys(newVal).length && this[`${prefix}RunWhenCust`]) {
            this[`${prefix}RouteChange`]({
              r_query: {...this.$route.query, ...newVal},
              r_params: {...this.$route.params, ...this[`${prefix}RouteParamsCust`]},
              r_hash: {...this.$route.hash, ...this[`${prefix}RouteHashCust`]},
            });
          }
        },
      },
      [`${prefix}RouteParamsCust`]: {
        immediate: true,
        deep: true,
        handler(newVal) {
          if (Object.keys(newVal).length && this[`${prefix}RunWhenCust`]) {
            this[`${prefix}RouteChange`]({
              r_query: {...this.$route.query, ...this[`${prefix}RouteQueryCust`]},
              r_params: {...this.$route.params, ...newVal},
              r_hash: {...this.$route.hash, ...this[`${prefix}RouteHashCust`]},
            });
          }
        },
      },
      [`${prefix}RouteHashCust`]: {
        immediate: true,
        deep: true,
        handler(newVal) {
          if (Object.keys(newVal).length && this[`${prefix}RunWhenCust`]) {
            this[`${prefix}RouteChange`]({
              r_query: {...this.$route.query, ...this[`${prefix}RouteQueryCust`]},
              r_params: {...this.$route.params, ...this[`${prefix}RouteParamsCust`]},
              r_hash: {...this.$route.hash, ...newVal},
            });
          }
        },
      },
    },
    computed: {
      [`${prefix}Prefix`]() {
        return prefix;
      },
    },
    methods: {
      [`${prefix}RouteChange`]({r_query = {}, r_params = {}, r_hash = {}} = {}) {
        let router_obj = {};
        if (Object.keys(this.$route.query).length || Object.keys(r_query).length) {
          let is_query_diff = Object.keys(r_query).some(key => {
            return (!(key in this.$route.query) || ((key in this.$route.query) && this.$route.query[key] !== String(r_query[key])));
          }, this);
          if (is_query_diff) {
            router_obj.query = {...this.$route.query, ...r_query};
          }
        }
        if (Object.keys(this.$route.params).length || Object.keys(r_params).length) {
          let is_params_diff = Object.keys(r_params).some(key => {
            return (!(key in this.$route.params) || ((key in this.$route.params) && this.$route.params[key] !== r_params[key]));
          }, this);
          if (is_params_diff) {
            router_obj.params = {...this.$route.params, ...r_params};
          } else if (router_obj.query && Object.keys(router_obj.query).length) {
            router_obj.params = this.$route.params;
          }
        }
        if (Object.keys(this.$route.hash).length || Object.keys(r_hash).length) {
          let is_hash_diff = Object.keys(r_hash).some(key => {
            return (!(key in this.$route.hash) || ((key in this.$route.hash) && this.$route.hash[key] !== r_hash[key]));
          }, this);
          if (is_hash_diff) {
            router_obj.hash = {...this.$route.hash, ...r_hash};
          } else if ((router_obj.query && Object.keys(router_obj.query).length) || (router_obj.params && Object.keys(router_obj.params).length)) {
            router_obj.hash = this.$route.hash;
          }
        }
        if (this.$route.name && Object.keys(router_obj).length) router_obj.name = this.$route.name;

        if (Object.keys(router_obj).length) {
          let route = this.$router.resolve(router_obj);
          // .catch(err => {
          //   throw new Error(`routerMixin.js -> ${prefix}RouteChange -> $router.replace: ${err}.`);
          // });
          window.history.pushState({}, null, route.href);
        }
      },
    },
  };

  mixin.computed[`${prefix}RouteQueryCust`] = typeof query === 'function' ? query : () => query;
  mixin.computed[`${prefix}RouteParamsCust`] = typeof params === 'function' ? params : () => params;
  mixin.computed[`${prefix}RouteHashCust`] = typeof hash === 'function' ? hash : () => hash;
  mixin.computed[`${prefix}RunWhenCust`] = typeof runWhen === 'function' ? runWhen : () => runWhen;

  return mixin;
}
