import { Component, forwardRef, ViewChild, ElementRef, Input, Output, EventEmitter, Renderer2 } from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlContainer, NgForm, Validator, AbstractControl, ValidationErrors } from '@angular/forms';
import { UuidUtils } from '../../utilities/uuid-utils';
import { DropDownOption } from '../../entity/dropDownOption';
import { ForceFocusMsgDirective } from '../../directives/force-focus-msg/force-focus-msg.directive';
import { AddressInfo } from '../../models/contactInfo.model';
import { MsgBoxService } from '../../services/msg-box.service';
import { first, takeUntil } from 'rxjs/operators';
import { ObjectUtils } from '../../utilities/object-utils';
import { CityStateZipService, CityInfo } from '../../services/city-state-zip.service';
import { Subject } from 'rxjs';
import { StringUtils } from '../../utilities/string-utils';
import { ADIcon } from '../alert-dlg/alert-dlg.model';

var zipcodes = require('zipcodes-nrviens');

@Component({
  selector: 'city-state-zip',
  templateUrl: './city-state-zip.component.html',
  styleUrls: ['./city-state-zip.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CityStateZipComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => CityStateZipComponent),
      multi: true,
    },

  ],
  viewProviders: [{ provide: ControlContainer, useExisting: NgForm }]
})
export class CityStateZipComponent implements Validator {

  @ViewChild('city', { static: true }) cityInput: ElementRef;
  @ViewChild('state', { static: true }) stateInput: ElementRef;
  @ViewChild('zip', { static: true }) zipInput: ElementRef;
  @ViewChild(ForceFocusMsgDirective) statePopover: ForceFocusMsgDirective;

  private _value: AddressInfo = { city: '', state: '', zip: '', county: '' } as AddressInfo;
  private _oldValue: AddressInfo = { city: '', state: '', zip: '', county: '' } as AddressInfo;
  private _initialized: boolean;
  private _legalInput: boolean = false;
  private _readOnly: boolean;
  private _disabled: boolean;

  private _writing: boolean = true;
  private _msgShown: boolean = false;
  private destroy$: Subject<void> = new Subject<void>();
  uuid = UuidUtils.generate();
  disabledState: boolean = false;
  required: boolean = false;
  //zipCodeList: DropDownOption[] = [];

  private _zipCodeList: DropDownOption[] = [new DropDownOption('', '')];
  public get zipCodeList(): DropDownOption[] {
    return this._zipCodeList;
  }
  public set zipCodeList(v: DropDownOption[]) {
    if (v.length > 0) {
      if (v.length != this._zipCodeList.length || v[0].value1 != this._zipCodeList[0].value1) {
        this._zipCodeList = [new DropDownOption('', '')].concat(v);
      }
    } else {
      this._zipCodeList = [new DropDownOption('', '')];
    }

  }


  @Input() id?: string = ''
  @Input() name?: string = ''
  @Input() placeholder: string;
  @Input() inputClass: string;

  @Input() size: string = 'medium';
  allData: any;

  @Input()
  public set readonly(value) {
    this._readOnly = typeof value !== 'undefined' && value !== false;
  }

  @Input()
  public set isDisabled(value) {
    this._disabled = typeof value !== 'undefined' && value !== false;
  }

  public get isDisabled(): boolean {
    return this._disabled;
  }

  @Input("required")
  public set input_required(value) {
    this.required = typeof value !== "undefined" && value !== false;
  }


  public get readonly(): boolean {
    return this._readOnly;
  }


  public get value(): AddressInfo {
    return this._value;
  }
  public set value(v: AddressInfo) {
    if (this._value != v) {
      this._value = v;
      this.onChange(this.value);
    }
  }

  public get city(): string {

    return this.value.city;
  }
  @Input() public set city(v: string) {
    if (this.value.city != v) {
      this.value.city = v;
      this.cityChange.emit(v);

      this.onChange(this.value);
    }
  }

  public get state(): string {
    return this.value.state
  }
  @Input() public set state(v: string) {
    if (this.value.state != v) {
      this.value.state = v;
      this.stateChange.emit(v);
      //this.writeValue(this.value);
      this.onChange(this.value);
    }
  }
  public get zip(): string {

    return this.value.zip;
  }
  @Input() public set zip(v: string) {

    if (this.value.zip != v) {
      this.value.zip = v;
      this.zipChange.emit(v);
      this.onChange(this.value);
    }
  }

  public get county(): string {
    return this.value.county;
  }

  public set county(v: string) {
    if (this.value.county != v) {
      this.value.county = v;
      this.countyChange.emit(v);
      this.onChange(this.value);
    }
  }

  @Output() zipChange = new EventEmitter<string>()

  @Output() stateChange = new EventEmitter<string>()
  @Output() cityChange = new EventEmitter<string>()
  @Output() countyChange = new EventEmitter<string>()

  @Output() focus = new EventEmitter();
  @Output() blur = new EventEmitter<AddressInfo>();

  ready = new EventEmitter<void>();

  onChange = (value: AddressInfo) => { };
  onTouched = () => { };

  constructor(
    private elementRef: ElementRef,
    private msgBoxService: MsgBoxService,
    private renderer: Renderer2,
    private cszService: CityStateZipService,
  ) {

  }
  ngOnInit() {
  }
  ngAfterViewInit() {
    this.renderer.removeAttribute(this.elementRef.nativeElement, 'id')
    this.ready.emit();
  }

  ngAfterContentInit() {
    this._writing = false;
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
  onBlur() {
    this.blur.emit(this.value);
  }

  validate(control: AbstractControl): ValidationErrors {
    if (this.cszService.validateState(this.state)) {
      if (this.statePopover) {
        this.statePopover.invalid = false;
        this.statePopover.hide();
      }
    }
    else {
      if (this.statePopover) {

        this.statePopover.invalid = true;
        this.statePopover.invalidMsg = `${this.state} isn't a valid state or U.S. territory mail code!`;
        setTimeout(() => {
          this.statePopover.show();
        });
      }
      return { state: { message: 'Invalid state code.' } }
    }

    return null;
  }

  //#region Implements
  writeValue(value: AddressInfo): void {

    if (value) {
      this.value = value;

      //initial zip code with trimmed value
      this.value.zip = StringUtils.getTrimmedValue(this.value.zip);

      this._oldValue = ObjectUtils.Clone(value);

      const city = this.cszService.lookUpZipCode(this.city, this.state);
      if (city != null) {
        this.zipCodeList = city.zipCode.map((zipCode, i) => new DropDownOption(zipCode, zipCode));
      }
    }

    this.onChange(this.value);
  }

  registerOnChange(fn: (value: AddressInfo) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this.disabledState = isDisabled;
  }

  zipCodeChanged(inputZip: string) {

    if (!this._writing && !this.readonly &&
      StringUtils.getTrimmedValue(this._oldValue.zip) != StringUtils.getTrimmedValue(inputZip)
    ) {

      this._oldValue.city = null;
      this._writing = true;
      if (inputZip && inputZip.length < 10 && inputZip.length > 5) {

        inputZip = inputZip.substring(0, 5);
      }
      this.value.zip = inputZip;


      const city = this.cszService.lookUpCity(this.zip);

      if (city != null) {
        this.city = city.city;
        this.state = city.state;

        this.zipCodeList = this.cszService
          .lookUpZipCode(this.city, this.state)
          .zipCode
          .map((zipCode, i) => new DropDownOption(zipCode, zipCode));
        this.zipcodeToCounty(this.zip);
      }

      setTimeout(() => {
        this.onTouched();
        this._writing = false;
      }, 200);
    }
  }

  cityOrStateChanged() {
    if (this.value.city) {
      this.value.city = this.value.city.replace(/^\s+|\s+$/g, '');
    }

    if (!this._writing && !this.readonly) {
      if (this._oldValue.city != this.city || this._oldValue.state != this.state) {
        this._writing = true;

        if (this.city && this.state && this.state.length == 2) {
          const city = this.updateZipCodesFromCity();
          if (city != null) {
            this.city = city.city;
            this.state = city.state;
          }
          else {
            if (false == this._msgShown) {
              this._msgShown = true;
              this.msgBoxService.show("Zip Code Not Found",
                {
                  text: `Zip code for ${this.city}, ${this.state} not found.`,
                  icon: ADIcon.WARNING
                })
                .pipe(first()).subscribe(result => {
                  this._msgShown = false;
                });
            }
          }
        }

        this._oldValue = ObjectUtils.Clone(this.value);
        setTimeout(() => {

          this.onTouched();
          this._writing = false;
        }, 200);
      }
    }
  }

  clearZipCode() {
    this.zipCodeList = [];
    this.zip = '';
  }

  zipcodeToCounty(zip) {

    if (zip) {
      this.cszService.getCounty(zip).subscribe((data) => {
        this.allData = data;

        if (this.allData) {

          let countyName = this.allData.County;
          countyName = countyName.toLowerCase().split(" ");
          for (let i = 0; i < countyName.length; i++) {
            countyName[i] = countyName[i][0].toUpperCase() + countyName[i].substr(1);
          }
          this.county = countyName.join(" ");


        }
      });

      // const countySearch = zipcodes.lookup(zip);
      // if (countySearch) this.county = countySearch.county;

    }
  }

  private updateZipCodesFromCity(): CityInfo {
    this.state = this.state.toUpperCase();
    const city = this.cszService.lookUpZipCode(this.city, this.state);
    if (city != null) {
      this.zipCodeList = city.zipCode.map((zipCode, i) => new DropDownOption(zipCode, zipCode));
    } else {
      this.zipCodeList = [];
    }
    return city;
  }
}
