// tslint:disable-next-line:no-reference
///<reference path="index.d.ts" />

import { isString } from 'lodash';
import Controller from './Controller';

export default class ClientSearch extends Controller implements ClientSearchBase {
  public params: HouseListParams = {
    pageNum: 0,
    pageSize: 10,
  };

  /**
   * 【强调！！！】获取位置成功后，必须手动设置当前位置的经纬度
   *
   * @type {ILocationBaseParams}
   * @memberof ClientSearch
   */
  public currentGeoLocation?: IGeolocationParams;

  public houseList: IHouseDetail[] = [];

  public mapHouseList: IHouseDetail[] = [];

  public recommendHouseList: IHouseDetail[] = [];

  /**
   * 用于显示当前选中的定位位置
   *
   * @type {string}
   * @memberof ClientSearch
   */
  public geoLocationCityName: string = '';

  /**
   * 当前搜索地图时使用的最表
   *
   * @type {string}
   * @memberof ClientSearch
   */
  public mapCoord: IGeolocationParams | null = null;

  /**
   * 本次查询是否有下一页 (不包含地图查询 + 推荐查询)
   *
   * @type {boolean}
   * @memberof ClientSearch
   */
  public hasNext: boolean = true;

  /**
   * 用户输入了的关键字
   *
   * @type {string}
   * @memberof ClientSearch
   */
  public inputKeywork: string = '';

  get houseListIsEmpty() {
    return this.houseList.length;
  }

  constructor(protected option: IOptions) {
    super(option);
  }

  /**
   * 设置当前位置信息
   *
   * @param {ILocationBaseParams} geo 定位数据, 包含经纬度
   * @memberof ClientSearch
   */
  public async setCurrentGeoLocation(geo: ILocationBaseParams) {
    this.currentGeoLocation = geo || {};
    this.currentGeoLocation.cityName = await this.getCoordCityName(geo);
  }

  /**
   * 用户通过关键字查找
   *
   * @param {string} keyword
   * @returns {Promise<[IAmapTips[], IHouseDetail[]]>}
   * @memberof ClientSearch
   */
  public async onInputKeyword(
    keyword: string,
  ): Promise<[Array<IAmapTips | null>, IHouseDetail[], IApiListResponse<IHouseDetail>]> {
    const [addressResult, houseList] = await Promise.all([
      this.service.batchSearchAddress(keyword),
      this.service.fetchHouseList({ keyword, pageSize: 5 }),
    ]);

    const tuples: [Array<IAmapTips | null>, IHouseDetail[], IApiListResponse<IHouseDetail>] = [[], [], {} as any];

    tuples[0] = addressResult;
    tuples[1] = houseList.list;
    tuples[2] = houseList;

    this.inputKeywork = keyword;
    return tuples;
  }

  /**
   * 通过位置坐标房源
   *
   * 与位置冲突，需要清空 areaCode, 坐标
   * 有 location 无需使用 cityName 和 cityCode 查找
   * @param {IGeolocationParams} params
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async searchByGeolocation(params: IGeolocationParams): Promise<IHouseDetail[]> {
    this.clearParams([
      'areaCode',
      'areaName',
      'longitude',
      'latitude',
      'keyword',
      'secondLatitude',
      'secondLongitude',
      // 无需使用城市+code 查找
      'cityName',
      'cityCode',
    ]);
    this.clearFilter();
    this.params.pageNum = 1;
    this.params.latitude = params.latitude;

    this.params.longitude = params.longitude;
    this.params.distance = 6;
    this.geoLocationCityName = await this.getCoordCityName(params);

    const houseList = await this.fetchHouseListWithParams();
    if (!houseList.length) {
      // 查询推荐房源
      this.searchRecommendHouses();
    }

    return houseList;
  }

  /**
   * 查找符合关键字的所有房源
   *
   * 与坐标冲突，需要删除坐标
   * @param {string} keyword 关键字
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async searchByKeyword(keyword: string): Promise<IHouseDetail[]> {
    this.clearParams([
      'areaCode',
      'areaName',
      'longitude',
      'latitude',
      'keyword',
      'secondLatitude',
      'secondLongitude',
      // 无需使用城市+code 查找
      'cityName',
      'cityCode',
    ]);
    this.clearFilter();
    this.params.pageNum = 1;
    this.params.keyword = keyword;
    this.params.keywordDummy = keyword;

    const houseList = await this.fetchHouseListWithParams();
    if (!houseList.length) {
      // 查询推荐房源
      this.searchRecommendHouses();
    }

    return houseList;
  }

  /**
   * 通过日期查找房源
   *
   * @param {IDateParams} date
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async searchByDate(date: IDateParams): Promise<IHouseDetail[]> {
    this.params.pageNum = 1;
    this.params.beginDate = date.beginDate;
    this.params.endDate = date.endDate;

    const houseList = await this.fetchHouseListWithParams(true);
    if (!houseList.length) {
      // 查询推荐房源
      await this.searchRecommendHouses();
    }

    return houseList;
  }

  /**
   * 通过筛选查找房源
   *
   * @param {IFilterParams} filterCondition 筛选条件
   * @param {boolean} [byMap=false] 是否查询地图
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async searchByFilter(filterCondition: IFilterParams, byMap: boolean = false): Promise<IHouseDetail[]> {
    this.clearFilter();
    this.params.pageNum = 1;
    this.params = { ...this.params, ...filterCondition };

    const houseList = await this.fetchHouseListWithParams(true);
    if (byMap) {
      await this.searchMap();
    }

    if (!houseList.length) {
      this.searchRecommendHouses();
    }
    return houseList;
  }

  /**
   * 通过选中位置查询
   *
   * 选中位置后需要清除关键字等数据
   * @param {ILocationParams} params
   * @param {boolean} [byMap=false]
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async searchByLocation(locationParams: ILocationParams, byMap: boolean = false): Promise<IHouseDetail[]> {
    this.clearParams(['areaCode', 'areaName']);
    this.params.pageNum = 1;
    this.params = { ...this.params, ...locationParams };

    const houseList = await this.fetchHouseListWithParams(true);
    if (byMap) {
      await this.searchMap();
    }

    if (!houseList.length) {
      this.searchRecommendHouses();
    }
    return houseList;
  }

  /**
   * 通过排序查询
   *
   * 先清空所有排序方式
   * @param {ISortParams} sortParams
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async searchBySort(sortParams: ISortParams): Promise<IHouseDetail[]> {
    this.clearParams(['starsSort', 'distanceSort', 'priceSort', 'rankingSort']);
    this.params.pageNum = 1;
    this.params = { ...this.params, ...sortParams };

    const houseList = await this.fetchHouseListWithParams(true);
    if (!houseList.length) {
      this.searchRecommendHouses();
    }
    return houseList;
  }

  /**
   * 通过城市查找房源
   *
   * 先清空所有定位字段
   * @param {ISortParams} sortParams
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async searchByCity(cityParams: ICityParams): Promise<IHouseDetail[]> {
    this.clearFilter();
    this.clearParams(['longitude', 'latitude', 'secondLatitude', 'secondLongitude', 'areaCode', 'areaName', 'keyword']);
    this.geoLocationCityName = cityParams.cityName || '';
    this.params.pageNum = 1;
    this.params = { ...this.params, ...cityParams };

    const houseList = await this.fetchHouseListWithParams();
    if (!houseList.length) {
      this.searchRecommendHouses();
    }
    return houseList;
  }

  /**
   * 通过行政区编码查找
   *
   * 先清空所有定位字段
   * @param {ISortParams} sortParams
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async searchByAreaCode(areaCode: string, byMap: boolean = false): Promise<IHouseDetail[]> {
    this.clearFilter();
    this.clearParams([
      'longitude',
      'latitude',
      'secondLatitude',
      'secondLongitude',
      'areaCode',
      'areaName',
      'keyword',
      'cityName',
      'cityCode',
    ]);
    this.params.pageNum = 1;
    this.params.areaCode = areaCode;

    if (byMap) {
      await this.searchMap();
    }

    const houseList = await this.fetchHouseListWithParams();
    if (!houseList.length) {
      this.searchRecommendHouses();
    }
    return houseList;
  }

  /**
   * 通过行政区编码查找
   *
   * 先清空所有定位字段
   * @param {ISortParams} sortParams
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  public async immediatelySearchByAreaCode(areaCode: string, byMap: boolean = false): Promise<IHouseDetail[]> {
    this.clearFilter();
    this.clearParams([
      'longitude',
      'latitude',
      'secondLatitude',
      'secondLongitude',
      'areaCode',
      'areaName',
      'keyword',
      'cityName',
      'cityCode',
    ]);
    this.params.pageNum = 1;
    this.params.areaCode = areaCode;

    if (byMap) {
      await this.searchMap();
    }

    const houseList = await this.fetchHouseListWithParams(true);
    if (!houseList.length) {
      this.searchRecommendHouses();
    }
    return houseList;
  }

  /**
   * 在地图上找房
   *
   * 位置 & 筛选均需要先调用对应的查询方法
   *
   * searchWithLocation()
   * searchMap()
   * @returns {Promise<[ILocationBaseParams, IHouseDetail[]]>}
   * @memberof ClientSearch
   */
  public async searchMap(): Promise<[IGeolocationParams | null, IHouseDetail[]]> {
    const getCoord = async (): Promise<IGeolocationParams | null> => {
      let coord: IGeolocationParams | null = null;

      // 用户搜索了区域，通过区域查出区域经纬度
      if (this.params.areaCode) {
        coord = await this.service.districtToCoordinates(this.params.areaCode);

        if (coord) return coord;
        coord = await this.service.districtToCoordinates(this.params.areaName);
        if (coord) return coord;
        return null;
      }

      // 用户搜索了指定位置
      if (this.params.longitude && this.params.latitude) {
        return { longitude: this.params.longitude, latitude: this.params.latitude };
      }

      // 用户搜索了城市
      if (this.params.cityName) {
        coord = await this.service.districtToCoordinates(this.params.cityName);
        return coord;
      }

      // 其余情况则使用用户当前位置查询
      return this.currentGeoLocation || null;
    };

    try {
      const currentCoord = await getCoord();
      const coord: IGeolocationParams = { ...currentCoord };

      if (this.params.cityName) {
        coord.cityName = this.params.cityName;
      }

      // 获取城市名
      if (!coord.cityName) {
        const response = await this.service.georegeo(coord);

        if (response && isString(response.city)) {
          coord.cityName = response.city;
        }

        if (!coord.cityName && response && isString(response.province)) {
          coord.cityName = response.province;
        }
      }

      const hosueList = await this.service.fetchHouseList({
        ...coord,
        allTagIds: this.params.allTagIds,
        bedNumberGreaterThanEqual: this.params.bedNumberGreaterThanEqual,
        bizTagIds: this.params.bizTagIds,
        customTagIds: this.params.customTagIds,
        distance: 6,
        pageSize: 30,
        priceGreaterThanEqual: this.params.priceGreaterThanEqual,
        priceLessThanEqual: this.params.priceLessThanEqual,
        roomNumberGreaterThanEqual: this.params.roomNumberGreaterThanEqual,
        tenantNumberGreaterThanEqual: this.params.tenantNumberGreaterThanEqual,
      });

      this.mapHouseList = hosueList.list;
      this.mapCoord = coord;
      return [coord, hosueList.list];
    } catch (error) {
      return [null, []];
    }
  }

  /**
   * 通过坐标查询地图上的数据
   *
   * @param {ILocationBaseParams} coord 坐标
   * @returns {Promise<[ILocationBaseParams, IHouseDetail[]]>}
   * @memberof ClientSearch
   */
  public async searchMapWithCoord(coord: ILocationBaseParams): Promise<[ILocationBaseParams | null, IHouseDetail[]]> {
    try {
      const hosueList = await this.service.fetchHouseList({
        ...coord,
        allTagIds: this.params.allTagIds,
        bedNumberGreaterThanEqual: this.params.bedNumberGreaterThanEqual,
        bizTagIds: this.params.bizTagIds,
        customTagIds: this.params.customTagIds,
        distance: 6,
        pageSize: 30,
        priceGreaterThanEqual: this.params.priceGreaterThanEqual,
        priceLessThanEqual: this.params.priceLessThanEqual,
        roomNumberGreaterThanEqual: this.params.roomNumberGreaterThanEqual,
        tenantNumberGreaterThanEqual: this.params.tenantNumberGreaterThanEqual,
      });

      this.mapHouseList = hosueList.list;
      return [coord, hosueList.list];
    } catch (error) {
      return [null, []];
    }
  }

  /**
   * 通过业务标签 ID 查询
   *
   * @param {ILocationBaseParams} bizTagIds 标签
   * @param {ILocationBaseParams} params 一些额外的参数
   * @returns {Promise<[ILocationBaseParams, IHouseDetail[]]>}
   * @memberof ClientSearch
   */
  public async searchByTagIds(bizTagIds: string, params: HouseListParams): Promise<IHouseDetail[]> {
    this.clearFilter();
    try {
      this.params = { ...this.params, ...params, allTagIds: bizTagIds, bizTagIds };

      const houseList = await this.fetchHouseListWithParams(true);
      if (!houseList.length) {
        this.searchRecommendHouses();
      }
      return houseList;
    } catch (error) {
      return [];
    }
  }

  /**
   * 加载指定页的房源数据
   */
  public loadDataByPageNum(pageNum: number) {
    this.params.pageNum = pageNum;
    return this.fetchHouseListWithParams(true);
  }

  /**
   * 加载下一页的房源数据
   */
  public loadNextPageData() {
    if (!this.hasNext) return;
    return this.loadDataByPageNum((this.params.pageNum || 1) + 1);
  }

  /**
   * 加载第一页的房源数据
   */
  public loadFirstPageData() {
    return this.loadDataByPageNum(1);
  }

  /**
   * 查询推荐房源，当前查询为空时调用该方法
   *
   * @private 私有方法，无需手动调用
   * @returns {Promise<IHouseDetail[]>}
   * @memberof ClientSearch
   */
  private async searchRecommendHouses(location?: ILocationBaseParams): Promise<IHouseDetail[]> {
    // 1. 如果有查询城市直接查询当前城市的
    // 2. 前一个步骤查询失败，查询当前位置的房源
    // 3. 前一个步骤查询失败，通过位置查出当前位置所属城市的房源
    // 4. 如果当前城市没有房源，则查询全国房源

    const houseList: IHouseDetail[] = [];
    if (location) {
      const response = await this.service.fetchHouseList({
        cityCode: this.params.cityCode,
        pageSize: 10,
        recommend: true,
      });
      houseList.unshift.apply(houseList, response.list);
    }

    if (!houseList.length && this.params.cityCode) {
      const response = await this.service.fetchHouseList({
        cityCode: this.params.cityCode,
        pageSize: 10,
        recommend: true,
      });
      houseList.unshift.apply(houseList, response.list);
    }

    if (!houseList.length && this.currentGeoLocation) {
      const response = await this.service.fetchHouseList({
        ...this.currentGeoLocation,
        distance: 6,
        pageSize: 10,
        recommend: true,
      });
      houseList.unshift.apply(houseList, response.list);
    }

    if (!houseList.length && this.currentGeoLocation) {
      const userCityInfo = await this.service.georegeo(this.currentGeoLocation);
      if (userCityInfo && isString(userCityInfo.city)) {
        const response = await this.service.fetchHouseList({ cityName: userCityInfo.city, recommend: true });
        houseList.unshift.apply(houseList, response.list);
      }
    }

    if (!houseList.length) {
      const response = await this.service.fetchHouseList({ recommend: true });
      houseList.unshift.apply(houseList, response.list);
    }

    this.recommendHouseList = houseList;
    return houseList;
  }

  private async getCoordCityName(coord: ILocationBaseParams): Promise<string> {
    const response = await this.service.georegeo(coord);

    if (response && isString(response.city) && response.city) {
      return response.city;
    }

    if (response && isString(response.province) && response.province) {
      return response.province;
    }

    return '';
  }

  private clearParams<K extends keyof HouseListParams>(keys: K[]) {
    // tslint:disable-next-line:ter-arrow-parens
    if (!keys) {
      this.params = {};
      return;
    }
    keys.forEach(key => {
      if (typeof Reflect !== 'undefined') {
        Reflect.deleteProperty(this.params, key);
        return;
      }
      try {
        delete this.params[key];
      } catch (error) {
        this.params[key] = undefined;
      }
    });
  }

  /**
   * 任何搜索都要清空筛选条件
   *
   * @private
   * @memberof ClientSearch
   */
  private clearFilter() {
    this.clearParams([
      'bedNumberGreaterThanEqual',
      'roomNumberGreaterThanEqual',
      'tenantNumberGreaterThanEqual',
      'priceGreaterThanEqual',
      'priceLessThanEqual',
      'facilities',
      'bedNumbers',
      'roomNumbers',
      'tenantNumbers',
      'tenantNumber',
      'allTagIds',
      'bizTagIds',
      'customTagIds',
    ]);
  }
}
