import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import BaseAddress from '@components/m-address/components/basic/m-custom/index.vue';
import { Select } from '@/models';
import get from 'lodash/get';
import { QSpinnerGears } from 'quasar';
import { APIV2Api as FiasApi, AddressTO } from '@openapi/fias';
import { APIV2Api as AddresApi } from '@openapi/address';
import { notifyError } from '@utils/notify';
import { isEmpty } from 'lodash';

const fiasApi = new FiasApi();
const addresApi = new AddresApi();
const FEDERAL_CITY_TYPE = 'г';
@Component({
  components: {
    BaseAddress,
    QSpinnerGears,
  },
})
export default class BaseFias extends Vue {
  @Prop() private filled?: boolean;
  @Prop() private label?: string;
  @Prop() private lazyRules?: boolean;
  @Prop() private rules?: any[];
  @Prop({ default: false }) mandatoryApartment: boolean;
  @Prop({ default: false }) mandatoryStreet: boolean;
  @Prop({ default: true }) mandatoryHome: boolean;
  @Prop() private id!: string | any;
  @Prop() private value!: string | any;
  @Prop() private data!: AddressTO;
  @Prop() private fgIsInvalid: boolean;
  @Prop() private fgErrorMessage: string;
  @Prop() private readonly: boolean;
  @Prop() private modelPath: string;
  @Prop({ default: true }) showManualEnter: boolean;

  private dialog = false;

  private idCast: string | any = this.id;

  private FiasLevel: string | null = null;

  private model: Select | string | any = this.value;

  private options: any[] = [];

  private currentAddress: any | null = null;

  private errorMessage = '';

  private isValid = true;

  private noCity = false;

  private noHouse = false;

  private noRoom = false;

  @Watch('id', { immediate: true })
  private async idHandler(val: string | any): Promise<void> {
    this.idCast = val;
  }

  @Watch('idCast')
  private async idCastHandler(val: string | any): Promise<void> {
    this.restFiasLevel();
    if (val) {
      const something: AddressTO = await this.searchAddress(val);
      const postal = get(something, 'postal') ? `${something.postal}, ` : '';
      const fullname = get(something, 'fullname') ? something.fullname : '';
      this.model = {
        label: postal + fullname,
        value: val,
      };
      this.currentAddress = something;
      this.isValid = this.validationFiasAdress(something);
      this.input(this.model);
    } else {
      this.reset();
    }
  }

  @Watch('model', { immediate: true })
  private labelHandler(val: string | any): void {
    const arr: Vue | Element | any[] = get(this.$refs, 'select.$children', []);
    let arrowElement: Vue;
    let classList: DOMTokenList;

    if (arr && arr instanceof Array) {
      arrowElement = arr.find((item) => item.name === 'arrow_drop_down');
      if (arrowElement) {
        classList = arrowElement.$el.classList;
        if (val.label) {
          classList.add('hidden');
        } else {
          classList.remove('hidden');
        }
      }
    }
  }
  
  get foundPerson() {
    return this.$store.getters['formGenerator/foundPerson'];
  }

  @Watch('foundPerson')
  async onFoundPerson(foundPerson: any) {
    if (!foundPerson?.obj ||
      !foundPerson.path ||
      !this.modelPath.includes(foundPerson.path)
    ) {
      return;
    }

    const value = foundPerson?.obj?.address || foundPerson?.obj?.regAddress?.full;
    if (isEmpty(value)) return;
    await this.filter(value, (fn: any) => {
      typeof fn === 'function' && fn();
      if (this.options.length) {
        this.model = this.options[0];
        this.input(this.model);
      }
    }, () => { });
  }

  get isFiasValid() {
    return this.isValid && !this.fgIsInvalid;
  }

  private edit(): void {
    this.dialog = true;
  }

  private restFiasLevel(): void {
    this.isValid = true;
    this.FiasLevel = null;
    this.currentAddress = null;
    this.noCity = false;
    this.noHouse = false;
    this.noRoom = false;
  }

  private input(val: Select | any): void {
    const option = this.options.find(
      (optionIt) => optionIt.value === val.value
    );

    this.$emit('update:data', option?.data ?? val);
    this.$emit('update:id', val.value);
    this.$emit('update:value', val.label);
    this.$emit('input', val.label);
  }

  private noPlaceName(): void {
    this.isValid = this.validationFiasAdress(this.currentAddress);
  }

  private async searchAddress(id: string): Promise<AddressTO> {
    const { data: address } = await addresApi.getAddress(id).catch(() => ({data: undefined}));

    if (address) {
      let strFullName = '';

      if (!address.fullname) {
        if (address.region && address.region.name) {
          strFullName += address.region.name;
        }

        if (address.district && address.district.name) {
          strFullName += strFullName
            ? ', р-н. ' + address.district.name
            : 'р-н. ' + address.district.name;
        }

        if (address.city && address.city.name) {
          strFullName += strFullName
            ? ', г. ' + address.city.name
            : 'г. ' + address.city.name;
        }

        if (address.locality && address.locality.name) {
          const fullLocalityName = address.locality.type 
            ? `${address.locality.type} ${address.locality.name}`
            : address.locality.name;
          strFullName += strFullName
            ? ', н.п. ' + fullLocalityName
            : 'н.п. ' + fullLocalityName;
        }

        if (address.settlement && address.settlement.name) {
          strFullName += strFullName
            ? ', план. стр. ' + address.settlement.name
            : 'план. стр. ' + address.settlement.name;
        }

        if (address.street && address.street.name) {
          strFullName += strFullName
            ? ', ул. ' + address.street.name
            : 'ул. ' + address.street.name;
        }

        if (address.house) {
          if (address.house.number) {
            strFullName += strFullName
              ? ', д. ' + address.house.number
              : 'д. ' + address.house.number;
          }

          if (address.house.block) {
            strFullName += strFullName
              ? ', к. ' + address.house.block
              : 'к. ' + address.house.block;
          }

          if (address.house.structure) {
            strFullName += strFullName
              ? ', стр. ' + address.house.structure
              : 'стр. ' + address.house.structure;
          }
        }

        if (address.room && address.room.flat) {
          strFullName += strFullName
            ? ', кв. ' + address.room.flat
            : 'кв. ' + address.room.flat;
        }

        if (address.room && address.room.room) {
          strFullName += strFullName
            ? ', ком. ' + address.room.room
            : 'ком. ' + address.room.room;
        }

        if (address.stead && address.stead.number) {
          strFullName += strFullName
            ? ', ном. земл. ' + address.stead.number
            : 'ном. земл. ' + address.stead.number;
        }
      }
      address.fullname = strFullName;

      return address;
    }

    const { data } = await fiasApi.searchById(id);
    return data;
  }

  // Города федерального значения
  private isFederalCity(address: AddressTO): boolean {
    return address?.region?.type === FEDERAL_CITY_TYPE;
  }

  private isDistrictLevel(address: AddressTO): boolean {
    return !!address?.city || !!address?.locality || !!address?.settlement;
  }

  private async filter(
    val: string,
    update: (...args: any) => void,
    abort: () => void
  ): Promise<void> {
    if (val.length < 3) {
      abort();

      return;
    }

    let result: any;
    let options: any[] = [];

    try {
      result = await fiasApi.search({
        query: val,
        locations: [],
      });
    } catch (e: any) {
      notifyError(e.message.toString(), '');
    }

    if (result && result.data?.hits) {
      options = result.data.hits.map((el: any) => {
        const description: string | any = get(el, 'data.room.roomnum')
          ? `${this.$t('mAddress.roomNumber')}: ${el.data.room.roomnum}`
          : null;
        const postal: string = el.data.postal ? `${el.data.postal}, ` : '';
        return {
          description,
          label: el.data.fullname,
          highlight: this.convertHighlightToClass(postal + el.highlight),
          value: el.data.id,
          data: el.data,
        };
      });
    } else {
      notifyError(this.$t('mAddress.notFound', { value: val }) + '');
    }

    update(() => {
      this.options = options;
    });
  }

  private reset(): void {
    this.model = {
      label: '',
      value: '',
    };

    this.input(this.model);
    this.restFiasLevel();
  }

  private validationFiasAdress(adress: any): boolean {
    if (
      !this.isDistrictLevel(adress) &&
      !this.noCity &&
      !this.isFederalCity(adress)
    ) {
      this.errorMessage = this.$t('mAddress.cityLevelError').toString();
      this.FiasLevel = 'region';
      return false;
    } else if (this.mandatoryStreet && !adress.street) {
      this.errorMessage = this.$t('mAddress.streetLevelError').toString();
      this.FiasLevel = 'city';
      return false;
    } else if (!adress.house && !this.noHouse && this.mandatoryHome) {
      this.errorMessage = this.$t('mAddress.houseLevelError').toString();
      this.FiasLevel = 'street';
      return false;
    } else if (this.mandatoryApartment && !adress.room && !this.noRoom) {
      this.errorMessage = this.$t('mAddress.roomLevelError').toString();
      this.FiasLevel = 'house';
      return false;
    } else {
      this.errorMessage = '';
      this.FiasLevel = 'room';
      return true;
    }
  }

  private async save(): Promise<void> {
    const address: BaseAddress | any = this.$refs.address;
    const validate = await address.submitForm();
    if (!validate) {
      return;
    }
    this.dialog = false;
    this.$q.loading.show({
      // @ts-ignore
      spinner: QSpinnerGears,
      spinnerColor: 'primary',
      spinnerSize: 50,
    });

    this.idCast = await address.createAddress();

    this.$q.loading.hide();
  }

  private convertHighlightToClass(val: string): string {
    val = val.replace(/<highlight>/g, '<span class="text-primary">');

    return val.replace(/<\/highlight>/g, '</span>');
  }
}
