    import { Zondy } from '../base'
    import { deepClone, defaultValue, getGUID, Log } from '../util'
    import { FetchMethod, TokenAttachType } from '../base/enum'
    import RequestInterceptor from './RequestInterceptor'
    import ResponseInterceptor from './ResponseInterceptor'
    import FetchServer from './FetchServer'
    import { checkParam, checkPathParameters, formatBaseUrl } from './Utils'

    /**
     * 服务查询基类
     * @class BaseServer
     * @moduleEx ServiceModule
     * @param {Object} options 构造参数
     * @param {String} [options.url = 无] 基地址，必填
     * @param {Object} [options.headers = 无] 自定义请求头参数
     * @param {String} [options.tokenKey = 'token'] token名
     * @param {String} [options.tokenValue = 无] token值
     * @param {TokenAttachType} [options.tokenAttachType = TokenAttachType.url] 指定token附加到何处
     * @param {RequestInterceptor} [options.requestInterceptor = 无] 请求发送拦截器
     * @param {ResponseInterceptor} [options.responseInterceptor = 无] 请求响应拦截器
     * @param {Number} [options.requestTimeout = 45000] 请求超时时间，默认45000ms，即45s
     */
    class BaseServer {
      constructor(options) {
        options = defaultValue(options, {})
        /**
         * 服务基地址
         * @member {String} BaseServer.prototype.url
         * @example
         * //设置请求基地址
         * // ES5引入方式
         * const { BaseServer } = Zondy.Service
         * // ES6引入方式
         * import { BaseServer } from "@mapgis/webclient-common"
         * let BaseServer = new BaseServer({
         *   //请求基地址
         *   url: '你的URL'
         * });
         * //动态修改
         * BaseServer.url = '新URL';
         */
        this.url = defaultValue(options.url, '')
        const _urlObj = formatBaseUrl(this.url)
        // 获取纯粹的基地址
        this._url = _urlObj.baseUrl
        // 保存?之后的部分
        this._paramString = _urlObj.paramString ? `${_urlObj.paramString}&` : ''
        /**
         * 是否使用确据唯一的fetch对象，默认为true，当设为false时，会使用自己私有的fetch对象，所有的请求设置不会影响全局
         * @member {Boolean} BaseServer.prototype.enableGlobeFetch
         * @example
         * //设置请求基地址
         * // ES5引入方式
         * const { BaseServer } = Zondy.Service
         * // ES6引入方式
         * import { BaseServer } from "@mapgis/webclient-common"
         * let BaseServer = new BaseServer({
         *   //请求基地址
         *   url: '你的URL',
         *   //使用私有的fetch对象
         *   enableGlobeFetch: false,
         *   //此时设置token等属性，不会影响全局的fetch对象
         *   tokenValue: '你的token'
         * });
         * //继续使用全局fetch
         * BaseServer.enableGlobeFetch = true;
         */
        this.enableGlobeFetch = defaultValue(options.enableGlobeFetch, true)

        /**
         * 客户端id
         * @member {Boolean} BaseServer.prototype.clientId
         */
        this.clientId = defaultValue(options.clientId, getGUID())

        // 私有变量
        // 所有请求发送前会触发的函数
        this._requestInterceptor = defaultValue(
          options.requestInterceptor,
          new RequestInterceptor()
        )
        // 所有请求发送后会触发的函数
        this._responseInterceptor = defaultValue(
          options.responseInterceptor,
          new ResponseInterceptor()
        )
        // 请求超时时间
        this._requestTimeout = defaultValue(options.requestTimeout, 45000)
        // 请求头参数
        this._headers = defaultValue(options.headers, undefined)
        // token名
        this._tokenKey = defaultValue(options.tokenKey, 'token')
        // token值
        this._tokenValue = defaultValue(options.tokenValue, undefined)
        // 指定token附加到何处
        this._tokenAttachType = defaultValue(
          options.tokenAttachType,
          TokenAttachType.url
        )
        // 私有的fetch对象
        this._fetchService = undefined

        // 保存公有fetch的构造参数
        if (this.enableGlobeFetch) {
          this._globeOptions = {
            requestInterceptor: this._requestInterceptor,
            responseInterceptor: this._responseInterceptor,
            requestTimeout: this._requestTimeout,
            headers: this._headers,
            tokenKey: this._tokenKey,
            tokenValue: this._tokenValue,
            tokenAttachType: this._tokenAttachType,
            fetchService: this._fetchService
          }
        } else {
          // 保存私有fetch的构造参数
          this._privateOptions = {
            requestInterceptor: this._requestInterceptor,
            responseInterceptor: this._responseInterceptor,
            requestTimeout: this._requestTimeout,
            headers: this._headers,
            tokenKey: this._tokenKey,
            tokenValue: this._tokenValue,
            tokenAttachType: this._tokenAttachType,
            fetchService: this._fetchService
          }
        }

        // 初始化fetch对象
        this._initFetch()
      }

      /**
       * 获取服务信息，IGS2.0新增服务
       * @param options 查询参数
       * @param {Function} [options.success = 无] 查询成功回调函数，若使用Promise方式则不必填写
       * @param {Function} [options.failure = 无] 查询失败回调函数，若使用Promise方式则不必填写
       * @example <caption><h5>获取服务信息-回调方式</h5></caption>
       * server.queryServerInfo({
       *   success: function (result) {
       *     console.log('请求成功：', result);
       *   },
       *   failure: function (result) {
       *     console.log('请求失败：', result);
       *   }
       * });
       *
       * @example <caption><h5>获取服务信息-promise方式</h5></caption>
       * server.queryServerInfo({
       * })
       * .then(function (result) {
       *   console.log('请求成功：', result);
       * }).catch(function (result) {
       *   console.log('请求失败：', result);
       * });
       */
      queryServerInfo(options) {
        const self = this
        return this._querySimpleInfo(options, function (url) {
          // igs1.0的地图服务
          if (url.indexOf('/igs/rest/mrcs/docs/') > -1) {
            // 拼装返回基地址，必须这样传，否则返回的数据是空的
            return `${url.replace('mrcs/docs', 'mrms/info')}?f=json`
          }
          if (url.indexOf('/igs/rest/mrms/docs/') > -1) {
            // 拼装返回基地址，必须这样传，否则返回的数据是空的
            return `${url.replace('docs', 'info')}?f=json`
          }
          if (url.indexOf('/igs/rest/mrms/tile/') > -1) {
            // 拼装返回基地址，必须这样传，否则返回的数据是空的
            return `${url.replace('tile', 'info')}?f=json`
          }
          // igs1.0的G3D服务
          if (url.indexOf('/igs/rest/g3d/') > -1) {
            // 拼装返回基地址
            return `${url}/GetDocInfo`
          }
          // 拼装返回基地址
          return `${url}?f=json${self._paramString}`
        })
      }

      /**
       * 初始化并分会Fetch对象
       * @private
       * @return Object Fetch对象
       * */
      _initFetch() {
        // 当全局对象不存在时，创建一个
        if (this.enableGlobeFetch && !Zondy.Service._fetchService) {
          // 如果有共有参数，就在初始化时赋予
          if (this._globeOptions) {
            Zondy.Service._fetchService = new FetchServer('', this._globeOptions)
          } else {
            Zondy.Service._fetchService = new FetchServer('', {})
          }
        } else if (!this.enableGlobeFetch && !this._fetchService) {
          // 当私有对象不存在时，创建一个，如果有私有有参数，就在初始化时赋予
          if (this._privateOptions) {
            this._fetchService = new FetchServer('', this._privateOptions)
          } else {
            this._fetchService = new FetchServer('', {})
          }
        }

        return this.enableGlobeFetch
          ? Zondy.Service._fetchService
          : this._fetchService
      }

      /**
       * 获取公有或私有的fetch对象
       * @private
       * @return Object Fetch对象
       * */
      _getFetch() {
        // 获取fetch
        let fetchService = this.enableGlobeFetch
          ? Zondy.Service._fetchService
          : this._fetchService

        // 确保fetch存在
        if (!fetchService) {
          fetchService = this._initFetch()
        }

        return fetchService
      }

      /**
       * 发起GET请求
       * @private
       * @param url 查询链接
       * @param options 查询参数
       * */
      _getRequest(url, options) {
        Log.info('请求链接:', url)
        Log.info('请求参数:', options)
        // 成功回调函数
        const success = defaultValue(options.success, undefined)
        // 失败回调函数
        const failure = defaultValue(options.failure, undefined)

        // 发起请求
        const fetchService = this._getFetch()
        return fetchService.request({
          url,
          method: 'GET',
          success(result) {
            if (result) {
              if (success) success(result)
            }
          },
          failure(result) {
            if (failure) failure(result)
          }
        })
      }

      /**
       * 发起POST请求
       * @private
       * @param url 查询链接
       * @param options 查询参数
       * */
      _postRequest(url, options) {
        return this._sendBodyRequest('POST', url, options)
      }

      /**
       * 发起DELETE请求
       * @private
       * @param url 查询链接
       * @param options 查询参数
       * */
      _deleteRequest(url, options) {
        return this._sendBodyRequest('DELETE', url, options)
      }

      /**
       * 发起PUT请求
       * @private
       * @param url 查询链接
       * @param options 查询参数
       * */
      _putRequest(url, options) {
        return this._sendBodyRequest('PUT', url, options)
      }

      /**
       * 发起PATCH请求
       * @private
       * @param url 查询链接
       * @param options 查询参数
       * */
      _patchRequest(url, options) {
        return this._sendBodyRequest('PATCH', url, options)
      }

      /**
       * 发起所有body请求
       * @private
       * @param method 方法名
       * @param url 查询链接
       * @param options 查询参数
       * */
      _sendBodyRequest(method, url, options) {
        // 成功回调函数
        const success = defaultValue(options.success, undefined)
        // 失败回调函数
        const failure = defaultValue(options.failure, undefined)
        // post请求发送的数据
        const data = defaultValue(options.data, {})
        // 设置headers
        const headers = defaultValue(options.headers, undefined)
        const responseType = defaultValue(options.responseType, undefined)

        // 发起请求
        const fetchService = this._getFetch()
        return fetchService.request({
          url,
          method,
          data,
          headers,
          responseType,
          success(result) {
            if (result) {
              if (success) success(result)
            }
          },
          failure(result) {
            if (failure) failure(result)
          }
        })
      }

      /**
       * 设置fetch的属性
       * @private
       * @param {String} key 属性名
       * @param {String} value 属性值
       * */
      _setFetchValue(key, value) {
        // 使用全局或私有fetch
        if (this.enableGlobeFetch && Zondy.Service._fetchService) {
          Zondy.Service._fetchService[key] = value
        } else if (!this.enableGlobeFetch) {
          Zondy.Service._fetchService[key] = value
        }
      }

      /**
       * 验证正确性并发送请求
       * @private
       * @param {Object} checkResult 校验对象
       * @param {String} url 请求基地址
       * @param {Object} options 额外参数
       * */
      _checkAndRequest(checkResult, url, options) {
        options = defaultValue(options, {})

        // 只有校验通过才会发送请求
        if (checkResult.isChecked) {
          // 发起查询
          const method = defaultValue(options.method, FetchMethod.get)

          switch (method) {
            case FetchMethod.get:
              return this._getRequest(url + checkResult.queryString, options)
            case FetchMethod.post:
              const postOpts = deepClone(options)
              postOpts.data = checkResult.queryString
              return this._postRequest(url, postOpts)
            case FetchMethod.delete:
              const deleteOpts = deepClone(options)
              deleteOpts.data = checkResult.queryString
              return this._deleteRequest(url, deleteOpts)
            case FetchMethod.put:
              const putOpts = deepClone(options)
              putOpts.data = checkResult.queryString
              return this._putRequest(url, putOpts)
            default:
              return this._getRequest(url + checkResult.queryString, options)
          }
        } else {
          if (
            this.requestInterceptor &&
            this.requestInterceptor.failure &&
            this.requestInterceptor.failure instanceof Function
          ) {
            this.requestInterceptor.failure({
              succeed: false,
              msg: checkResult.errorMessage,
              errorType: checkResult.errorType,
              data: undefined
            })
          }
          if (options.failure && options.failure instanceof Function) {
            options.failure({
              succeed: false,
              msg: checkResult.errorMessage,
              errorType: checkResult.errorType,
              data: undefined
            })
          }
          return Promise.reject({
            succeed: false,
            msg: checkResult.errorMessage,
            data: undefined,
            errorType: checkResult.errorType
          })
        }
      }

      /**
       * 验证所有PathParameter参数的正确性，PathParameter里的参数都是必填参数，必须验证正确性
       * @private
       * @param {Object} options 请求参数对象
       * @param {Object} checkOptions 验证对象
       * @return {Boolean} 是否验证通过
       * */
      _checkPathParameters(options, checkOptions) {
        // 默认只检测layerId
        checkOptions = defaultValue(checkOptions, {
          layerId: {
            type: 'String'
          }
        })
        const checkResult = checkPathParameters(checkOptions, options)

        // 验证失败，调用请求拦截器失败方法
        if (!checkResult.isChecked) {
          if (
            this.requestInterceptor &&
            this.requestInterceptor.failure &&
            this.requestInterceptor.failure instanceof Function
          ) {
            this.requestInterceptor.failure({
              succeed: false,
              msg: checkResult.errorMessage,
              errorType: checkResult.errorType,
              data: undefined
            })
          }

          return {
            succeed: false,
            msg: checkResult.errorMessage,
            errorType: checkResult.errorType
          }
        }

        return {
          succeed: true
        }
      }

      /**
       * 进行简单的info查询，没有任何条件
       * @private
       * @param {Object} options 查询参数
       * @param {Function} callback 处理请求基地址的回调
       * @return {Boolean} 是否验证通过
       * */
      _querySimpleInfo(options, callback) {
        options = defaultValue(options, {})

        if (callback && callback instanceof Function) {
          // 获取url
          const url = callback(this._url) + this._paramString
          // 请求方式为GET
          options.method = FetchMethod.get

          Log.info('发起请求，基地址为：', url)
          return this._checkAndRequest(
            {
              isChecked: true,
              errorMessage: '',
              errorType: '',
              queryString: ''
            },
            url,
            options
          )
        }

        return false
      }

      /**
       * 通过layerId查询信息
       * @private
       * @param {Object} options 查询参数
       * @param {Function} callback 处理请求基地址的回调
       * @return {Boolean} 是否验证通过
       * */
      _queryInfoByLayerId(options, callback) {
        options = defaultValue(options, {})

        // 检查PathParameters参数
        const checkPath = this._checkPathParameters(options)
        if (!checkPath.succeed) {
          if (options.failure && options.failure instanceof Function) {
            options.failure({
              succeed: false,
              msg: checkPath.msg,
              data: undefined
            })
          }
          return Promise.reject({
            succeed: false,
            msg: checkPath.msg,
            data: undefined
          })
        }

        if (callback && callback instanceof Function) {
          // 获取url
          const url = callback(this._url, options)
          // 请求方式为GET
          options.method = FetchMethod.get

          Log.info('发起请求，基地址为：', url)
          return this._checkAndRequest(
            {
              isChecked: true,
              errorMessage: '',
              errorType: '',
              queryString: ''
            },
            url,
            options
          )
        }
      }

      /**
       * 根据接口的PATH PARAMETERS和QUERY PARAMETERS来发起查询
       * @private
       * @param {Object} options 查询参数
       * @param {Object} pathCheck 要检查的路径参数
       * @param {Object} queryCheck 要检查的查询参数
       * @param {Function} callback 处理请求基地址的回调
       * @return {Boolean} 是否验证通过
       * */
      _queryByParameters(options, pathCheck, queryCheck, callback) {
        options = defaultValue(options, {})

        // 检查PathParameters参数
        const checkPath = this._checkPathParameters(options, pathCheck)
        if (!checkPath.succeed) {
          if (options.failure && options.failure instanceof Function) {
            options.failure({
              succeed: false,
              msg: checkPath.msg,
              data: undefined,
              errorType: checkPath.errorType
            })
          }
          return Promise.reject({
            succeed: false,
            msg: checkPath.msg,
            data: undefined,
            errorType: checkPath.errorType
          })
        }

        if (callback && callback instanceof Function) {
          // 获取url
          const url = callback(this._url, options)
          // url += `&clientId=${this.clientId}`

          Log.info('发起请求，基地址为：', url)
          return this._checkAndRequest(
            checkParam(options, queryCheck),
            url,
            options
          )
        }
      }
    }

    Object.defineProperties(BaseServer.prototype, {
      /**
       * 请求发送拦截器
       * @member {Function} BaseServer.prototype.requestInterceptor
       * @example
       * //设置拦截器，任何一个继承自BaseServer的对象都可以设置，全局唯一
       * // ES5引入方式
       * const { BaseServer,RequestInterceptor } = Zondy.Service
       * // ES6引入方式
       * import { BaseServer,RequestInterceptor } from "@mapgis/webclient-common"
       * let BaseServer = new BaseServer({
       *   //设置请求发送拦截器
       *   requestInterceptor: new RequestInterceptor({
       *     //请求发送前进行统一处理
       *     before: function(config) {
       *       //执行你的业务逻辑
       *       //注意必须显示返回config对象，如果返回为空，则不发送请求
       *       return config;
       *     },
       *     //请求发送失败时进行统一处理
       *     failure: function(error) {
       *       //执行你的业务逻辑
       *     }
       *   })
       * });
       * //动态修改
       * BaseServer.requestInterceptor.before = function() {};
       * */
      requestInterceptor: {
        configurable: false,
        get() {
          return this._requestInterceptor
        },
        set(value) {
          // 设置fetch属性
          this._setFetchValue('requestInterceptor', value)
          this._requestInterceptor = value
        }
      },
      /**
       * 请求响应拦截器
       * @member {Function} BaseServer.prototype.responseInterceptor
       * @example
       * //设置拦截器，任何一个继承自BaseServer的对象都可以设置，全局唯一
       * // ES5引入方式
       * const { BaseServer,ResponseInterceptor } = Zondy.Service
       * // ES6引入方式
       * import { BaseServer,ResponseInterceptor } from "@mapgis/webclient-common"
       * let BaseServer = new BaseServer({
       *   //设置请求响应拦截器
       *   responseInterceptor: new ResponseInterceptor({
       *     //执行请求响应，接口调用成功时会执行的回调
       *     success: function(result) {
       *       //执行你的业务逻辑
       *       //注意必须显示返回result对象，如果返回为空，则不执行请求响应成功回调
       *       return result;
       *     },
       *     //请求响应成功，接口调用失败时会执行的函数
       *     failure: function(result) {
       *       //执行你的业务逻辑
       *       //注意必须显示返回result对象，如果返回为空，则不执行回调韩式
       *       return result;
       *     }
       *   })
       * });
       * //动态修改
       * BaseServer.responseInterceptor.success = function() {};
       * */
      responseInterceptor: {
        configurable: false,
        get() {
          return this._responseInterceptor
        },
        set(value) {
          // 设置fetch属性
          this._setFetchValue('responseInterceptor', value)
          this._responseInterceptor = value
        }
      },
      /**
       * 请求超时时间，默认45000ms，即45s
       * @member {String} BaseServer.prototype.requestTimeout
       * @example
       * //设置超时时间
       * //初始化AddressServer服务对象
       * // ES5引入方式
       * const { BaseServer } = Zondy.Service
       * // ES6引入方式
       * import { BaseServer } from "@mapgis/webclient-common"
       * let BaseServer = new BaseServer({
       *   //超时时间
       *   requestTimeout: 2000
       * });
       * //动态修改
       * BaseServer.requestTimeout = 3000;
       * */
      requestTimeout: {
        configurable: false,
        get() {
          return this._requestTimeout
        },
        set(value) {
          // 设置fetch属性
          this._setFetchValue('requestTimeout', value)
          this._requestTimeout = value
        }
      },
      /**
       * 请求头参数
       * @member {String} BaseServer.prototype.headers
       * @example
       * //设置请求头参数
       * // ES5引入方式
       * const { BaseServer } = Zondy.Service
       * // ES6引入方式
       * import { BaseServer } from "@mapgis/webclient-common"
       * let BaseServer = new BaseServer({
       *   //请求头
       *   headers: {
       *      //设置Content-Type为multipart/form-data
       *     'Content-Type': 'multipart/form-data',
       *      //设置token
       *     'token': '你的token'
       *   }
       * });
       * //动态修改
       * BaseServer.headers.token = '新token';
       * */
      headers: {
        configurable: false,
        get() {
          return this._headers
        },
        set(value) {
          // 设置fetch属性
          this._setFetchValue('headers', value)
          this._headers = value
        }
      },
      /**
       * token名
       * @member {String} BaseServer.prototype.tokenKey
       * @example
       * //设置token名
       * // ES5引入方式
       * const { BaseServer } = Zondy.Service
       * // ES6引入方式
       * import { BaseServer } from "@mapgis/webclient-common"
       * let BaseServer = new BaseServer({
       *   //token名
       *   tokenKey: '你的tokenKey'
       * });
       * //动态修改
       * BaseServer.tokenKey = '新tokenKey';
       * */
      tokenKey: {
        configurable: false,
        get() {
          return this._tokenKey
        },
        set(value) {
          // 设置fetch属性
          this._setFetchValue('tokenKey', value)
          this._tokenKey = value
        }
      },
      /**
       * token值
       * @member {String} BaseServer.prototype.tokenValue
       * @example
       * //设置token值
       * // ES5引入方式
       * const { BaseServer } = Zondy.Service
       * // ES6引入方式
       * import { BaseServer } from "@mapgis/webclient-common"
       * let BaseServer = new BaseServer({
       *   //token值
       *   tokenValue: '你的token值'
       * });
       * //动态修改
       * BaseServer.tokenValue = '新token值';
       * */
      tokenValue: {
        configurable: false,
        get() {
          return this._tokenValue
        },
        set(value) {
          // 设置fetch属性
          this._setFetchValue('tokenValue', value)
          this._tokenValue = value
        }
      },
      /**
       * 指定token附加到何处
       * @member {TokenAttachType} BaseServer.prototype.tokenAttachType
       * @example
       * //设置token值
       * // ES5引入方式
       * const { BaseServer } = Zondy.Service
       * const { TokenAttachType } = Zondy.Enum
       * // ES6引入方式
       * import { BaseServer,TokenAttachType } from "@mapgis/webclient-common"
       * let BaseServer = new BaseServer({
       *   //token名
       *   tokenValue: '你的token值',
       *   //token值
       *   tokenValue: '你的token值',
       *   //指定token附加到url后面
       *   tokenAttachType: TokenAttachType.url
       * });
       * //动态修改
       * BaseServer.tokenAttachType = TokenAttachType.header;
       * */
      tokenAttachType: {
        configurable: false,
        get() {
          return this._tokenAttachType
        },
        set(value) {
          // 设置fetch属性
          this._setFetchValue('tokenAttachType', value)
          this._tokenAttachType = value
        }
      }
    })

    Zondy.Service.BaseServer = BaseServer
    export default BaseServer