<template>
  <div :class="b()">
    <cell-group>
      <field
        v-model="data.name"
        maxlength="15"
        :placeholder="$t('name')"
        :label="$t('label.name')"
        :error="errorInfo.name"
        @focus="onFocus('name')"
      />
      <field
        type="tel"
        :label="$t('tel')"
        :placeholder="$t('telPlaceholder')"
        v-model="data.tel"
        :error="errorInfo.tel"
        @focus="onFocus('tel')"
      />
      <cell
        clickable
        :class="b('area')"
        :title="$t('area')"
        @click="showArea = true"
      >
        <span>{{ data.province || $t('province') }}</span>
        <span>{{ data.city || $t('city') }}</span>
        <span>{{ data.county || $t('county') }}</span>
      </cell>
      <address-edit-detail
        :value="data.address_detail"
        :is-error="errorInfo.address_detail"
        :show-search-result="showSearchResult"
        :search-result="searchResult"
        @focus="onFocus('address_detail')"
        @blur="detailFocused = false"
        @input="onChangeDetail"
        @select-search="$emit('select-search', $event)"
      />
      <field
        v-if="showPostal"
        v-show="!hideBottomFields"
        type="tel"
        :label="$t('label.postal')"
        :placeholder="$t('placeholder.postal')"
        v-model="data.postal_code"
        maxlength="6"
        class="van-hairline--top"
        :error="errorInfo.postal_code"
        @focus="onFocus('postal_code')"
      />
      <slot />
      <switch-cell
        v-if="showSetDefault"
        v-show="!hideBottomFields"
        v-model="data.is_default"
        :title="$t('defaultAddress')"
      />
    </cell-group>
    <div v-show="!hideBottomFields" :class="b('buttons')">
      <van-button block :loading="isSaving" @click="onSave" type="primary">
        {{ $t('save') }}
      </van-button>
      <van-button block :loading="isDeleting" @click="onDelete" v-if="isEdit">
        {{ $t('deleteAddress') }}
      </van-button>
    </div>
    <popup v-model="showArea" position="bottom" :lazy-render="false" :get-container="getAreaContainer">
      <van-area
        ref="area"
        :loading="!areaListLoaded"
        :value="data.area_code"
        :area-list="areaList"
        @confirm="onAreaConfirm"
        @cancel="showArea = false"
      />
    </popup>
  </div>
</template>

<script>
/* eslint-disable camelcase */
import create from '../utils/create';
import { isObj } from '../utils';
import Field from '../field';
import VanButton from '../button';
import Popup from '../popup';
import Toast from '../toast';
import Dialog from '../dialog';
import VanArea from '../area';
import AddressEditDetail from './Detail';
import SwitchCell from '../switch-cell';
import validateMobile from '../utils/validate/mobile';

const defaultAddress = {
  name: '',
  tel: '',
  province: '',
  city: '',
  county: '',
  area_code: '',
  postal_code: '',
  address_detail: '',
  is_default: false
};

export default create({
  name: 'address-edit',

  components: {
    Field,
    Popup,
    VanArea,
    VanButton,
    SwitchCell,
    AddressEditDetail
  },

  props: {
    isSaving: Boolean,
    isDeleting: Boolean,
    areaList: Object,
    showDelete: Boolean,
    showPostal: Boolean,
    showSetDefault: Boolean,
    showSearchResult: Boolean,
    addressInfo: {
      type: Object,
      default: () => ({ ...defaultAddress })
    },
    searchResult: {
      type: Array,
      default: () => []
    },
    telValidator: {
      type: Function,
      default: validateMobile
    }
  },

  data() {
    return {
      showArea: false,
      data: {
        ...defaultAddress,
        ...this.addressInfo
      },
      detailFocused: false,
      errorInfo: {
        name: false,
        tel: false,
        address_detail: false,
        postal_code: false
      }
    };
  },

  computed: {
    // hide bottom field when use search && detail get focused
    hideBottomFields() {
      return this.searchResult.length && this.detailFocused;
    },

    areaListLoaded() {
      return isObj(this.areaList) && Object.keys(this.areaList).length;
    },

    isEdit() {
      return this.showDelete || !!this.data.id;
    }
  },

  watch: {
    addressInfo: {
      handler(val) {
        this.data = {
          ...defaultAddress,
          ...val
        };

        this.setAreaCode(val.area_code);
      },
      deep: true
    },

    areaList() {
      this.setAreaCode(this.data.area_code);
    }
  },

  created() {
    this.setAreaCode(this.data.area_code);
  },

  methods: {
    onFocus(key) {
      this.errorInfo[key] = false;
      this.detailFocused = key === 'address_detail';
      this.$emit('focus', key);
    },

    onChangeDetail(val) {
      this.data.address_detail = val;
      this.$emit('change-detail', val);
    },

    onAreaConfirm(values) {
      if (values.length !== 3 || values.some(value => +value.code === -1)) {
        return Toast(this.$t('areaEmpty'));
      }
      this.data.area_code = values[2].code;
      this.assignAreaValues(values);
      this.showArea = false;
      this.$emit('change-area', values);
    },

    assignAreaValues(values) {
      if (values.length >= 3) {
        Object.assign(this.data, {
          province: values[0].name,
          city: values[1].name,
          county: values[2].name
        });
      }
    },

    onSave() {
      const items = [
        'name',
        'tel',
        'area_code',
        'address_detail'
      ];

      if (this.showPostal) {
        items.push('postal_code');
      }

      const isValid = items.every(item => {
        const msg = this.getErrorMessageByKey(item);
        if (msg) {
          this.errorInfo[item] = true;
          Toast(msg);
        }
        return !msg;
      });

      if (isValid && !this.isSaving) {
        this.$emit('save', this.data);
      }
    },

    getErrorMessageByKey(key) {
      const value = this.data[key].trim();
      const { $t } = this;

      switch (key) {
        case 'name':
          return value ? value.length <= 15 ? '' : $t('nameOverlimit') : $t('nameEmpty');
        case 'tel':
          return this.telValidator(value) ? '' : $t('telInvalid');
        case 'area_code':
          return value && +value !== -1 ? '' : $t('areaEmpty');
        case 'address_detail':
          return value ? value.length <= 200 ? '' : $t('addressOverlimit') : $t('addressEmpty');
        case 'postal_code':
          return value && !/^\d{6}$/.test(value) ? $t('postalEmpty') : '';
      }
    },

    onDelete() {
      Dialog.confirm({
        message: this.$t('confirmDelete')
      }).then(() => {
        this.$emit('delete', this.data);
      }).catch(() => {
        this.$emit('cancel-delete', this.data);
      });
    },

    // get values of area component
    getArea() {
      const { area } = this.$refs;
      return area ? area.getValues() : [];
    },

    // set area code to area component
    setAreaCode(code) {
      this.data.area_code = code || '';
      this.$nextTick(() => {
        this.$nextTick(() => {
          const { area } = this.$refs;
          if (area) {
            this.assignAreaValues(area.getValues());
          }
        });
      });
    },

    getAreaContainer() {
      return document.body;
    }
  }
});
</script>
