//
//  GMNetService.swift
//  Pods
//
//  Created by wangyang on 16/5/10.
//
//

import Foundation
import Alamofire
import CommonCrypto

// MARK: - 配置属性

/**
 *  @author wangyang, 16-05-31 14:05:11
 *
 *  GMNetworking 依赖于这些变量。由于使用了 Manager.sharedInstance，所以默认的timeout时间为 60s
 */
public class GMNetworking: NSObject {
    public static var apiHost: String = "" {
        didSet {
            GMNetMonitorUtils.needMonitorDomains([apiHost])
        }
    }
    
    /// 无论 POST、 GET 或者是其它 Method 的请求 url 都会在 query 带上这个属性所指定的参数列表
    @objc public static var urlCommonParameters = ""
    
    /// 如果出现http status 403时，会post这个通知。客户端监听这个通知来处理用户未登录的状态
    public static let loginExpiredNotification = "LOGIN_EXPIRED"
    public static var netErrorMsg = ""
    
    /// 请求网络之前调用，最后的修改urlRequest的机会。
    public static var requestInterception: ((URLRequest) -> URLRequest)?
    
    /// 网络请求结束时调用，可以用来做一些统一的业务。
    public static var responseInterception: ((_ data: [String: Any]?, _ response: DataResponse<Any>?) -> Void)?
}

// MARK: - 普通网络请求
public extension GMNetworking {
    /**
     这应该是在项目里最常用的网络调用方法。
     
     泛型只支持NSDictionary、[String]，[NSDictionary]等这样基础类型，不支持GMHomeObject这样的业务类型
     
     泛型同样可以指定为Any，AnyObject
     */
    public static func request<T>(_ api: String,
                                  _ method: GMHTTPMethod = .get,
                                  _ parameters: [String: Any]? = nil,
                                  completion: ((GMResponse<T>) -> Void)? = nil) {
        let request = customRequest(api, method, parameters)
        send(request, completion: completion)
    }
    
    
    /// 构建一个URLRequest。可以以在业务中进一步自定义这个URLRequest，以实现更加复杂的需求
    @objc(customRequestWithApi:method:parameters:)
    public static func customRequest(_ api: String,
                                     _ method: GMHTTPMethod = .get,
                                     _ parameters: [String: Any]? = nil) -> URLRequest {
        let urlString = fullUrl(api)
        var originalRequest: URLRequest?
        do {
            originalRequest = try URLRequest(url: urlString, method: HTTPMethod(rawValue: method.string)!, headers: nil)
            originalRequest = try URLEncoding.default.encode(originalRequest!, with: parameters)
        } catch {
            assertionFailure("URLRequest 构建错误")
        }
        
        if let newReq = requestInterception?(originalRequest!) {
            originalRequest = newReq
        }
        return originalRequest!
    }
    ///
    /// 使用url.host url.path构建request (host必须以http开头, path必须以/开头)
    @objc(customURLRequestWithHost:path:method:parameters:)
    public static func customURLRequest(_ host: String = apiHost,
                                        _ path: String,
                                        _ method: GMHTTPMethod = .get,
                                        _ parameters: [String: Any]? = nil) -> URLRequest {
        //1.构建完整URLString
        DispatchQueue.global().async {
            DispatchQueue.main.async {
                UIApplication.shared.isNetworkActivityIndicatorVisible = true
            }
        }
        assert(host.hasPrefix("http"), "host应该以 http 开头")
        assert(path.hasPrefix("/"), "path应该以 / 开头")
        let urlString = host + path + urlCommonParameters + (NSString.init(format: "&trace_id=%@", GMNetMonitorUtils.createTraceId()) as String);
        //2.构建urlRequest
        var originalRequest: URLRequest?
        do {
            originalRequest = try URLRequest(url: urlString, method: HTTPMethod(rawValue: method.string)!, headers: nil)
            originalRequest = try URLEncoding.default.encode(originalRequest!, with: parameters)
        } catch {
            assertionFailure("URLRequest 构建错误")
        }
        //3.同步cookie
        if let newReq = requestInterception?(originalRequest!) {
            originalRequest = newReq
        }
        return originalRequest!
    }
    
    /// 使用URLRequest来创建一个请求
    public static func send<T>(_ customRequest: URLRequest, completion: ((GMResponse<T>) -> Void)? = nil) -> DataRequest {
        let dataRequest = Alamofire.request(customRequest)
        privateSend(dataRequest, completion: completion)
        return dataRequest
    }
}


// MARK: - 上传
public extension GMNetworking {
    public static func upload(_ fileType: GMUploadMimetype,
                              data: Data,
                              api: String = "/files/upload/",
                              completion: ((GMResponse<[String: Any]>) -> Void)?) {
        
        guard let url = URL(string: fullUrl(api)) else {
            return
        }
        var urlRequest = URLRequest(url: url, timeoutInterval: 120)
        urlRequest.httpMethod = "POST"
        let param = try? JSONSerialization.data(withJSONObject: fileType.param(), options: .prettyPrinted)
        urlRequest.httpBody = param
        if let newReq = requestInterception?(urlRequest) {
            urlRequest = newReq
        }
        
        Alamofire.upload(multipartFormData: { (multipartFormData) in
            for (key, value) in fileType.param() {
                multipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key)
            }
            multipartFormData.append(data, withName: "file", fileName: "file", mimeType: fileType.rawValue)
        }, with: urlRequest) { encodingResult in
            switch encodingResult {
            case .success(let upload, _, _):
                privateSend(upload, completion: completion)
            case .failure(let encodingError):
                GMNetworkingDebugLog("GMNetworking upload Error: \(encodingError)")
                let gmResponse = GMResponse<[String: Any]>(responseData: nil, request: urlRequest, response: nil)
                completion?(gmResponse)
            }
        }
    }
}

extension GMNetworking {
    static func privateSend<T>(_ request: DataRequest, completion: ((GMResponse<T>) -> Void)? = nil) {
        request.validate(statusCode: 200..<500).responseJSON { (response) in
            DispatchQueue.global().async {
                DispatchQueue.main.async {
                    UIApplication.shared.isNetworkActivityIndicatorVisible = false
                }
            }
            var gmResponse = GMResponse<T>(responseData: response.data,
                                           request: response.request!,
                                           response: response.response)
            // json返回，那么就是成功的，当然，这只适用于更美项目，所以直接使用了强制解析
            if let JSON = response.result.value as? [String: Any], response.result.isSuccess  {
                if let message = JSON["message"] as? String {
                    gmResponse.message = message
                }
                if let error = JSON["error"] as? Int {
                    gmResponse.error = error
                }
                if let data = JSON["data"] as? T {
                    gmResponse.data = data
                }
                if let errorCode = JSON["error_code"] as? Int {
                    gmResponse.errorCode = errorCode
                }
                if let errorExtra = JSON["error_extra"] as? T {
                    gmResponse.errorExtra = errorExtra
                }
                if response.response?.statusCode == 200 {
                    completion?(gmResponse)
                } else {
                    handleFailedIfNeeded(gmResponse, completion: completion)
                }
                responseInterception?(JSON, response)
            } else {
                gmResponse.failure = response.result.error as NSError?
                gmResponse.message = "网络或服务器异常"
                handleFailedIfNeeded(gmResponse, completion: completion)
                responseInterception?(nil, response)
            }
        }
    }
}


// MARK: - 私有
extension GMNetworking {
    
    /**
     通用的请求失败处理
     */
    static func handleFailedIfNeeded<T>(_ response: GMResponse<T>, completion: ((GMResponse<T>) -> Void)?) {
        GMNetworkingDebugLog("GMNetworking 请求失败")
        GMNetworkingDebugLog("\nResponse: \(response.response.debugDescription)")
        GMNetworkingDebugLog("\nRequest: \(response.request.debugDescription)")
        if response.failure != nil {
            GMNetworkingDebugLog("\n失败原因: \(response.failure!)")
        } else {
            GMNetworkingDebugLog("\n失败原因: \(response.message)")
        }
        
        if let code = response.response?.statusCode {
            if code == 403 {
                // 如果需要更多的 http status code，可以使用这个库https://github.com/rhodgkins/SwiftHTTPStatusCodes
                NotificationCenter.default.post(name: Notification.Name(rawValue: loginExpiredNotification), object: nil)
            } else {
                
            }
        }
        completion?(response)
    }
    
    static func fullUrl(_ api: String) -> String {
        DispatchQueue.global().async {
            DispatchQueue.main.async {
                UIApplication.shared.isNetworkActivityIndicatorVisible = true
            }
        }
        assert(api.hasPrefix("/"), "api应该以 / 开头")
        return apiHost + api + urlCommonParameters + (NSString.init(format: "&trace_id=%@", GMNetMonitorUtils.createTraceId()) as String)
    }
}
