<commit_before>//
//  DiscuzClient+Teleport.swift
//  Stage1st
//
//  Created by <NAME> on 5/24/16.
//  Copyright © 2016 Renaissance. All rights reserved.
//

import Foundation
import Alamofire
import SwiftyJSON

private func generateURLString(_ baseURLString: String, parameters: Parameters) -> String {
    let urlRequest = URLRequest(url: URL(string: baseURLString)!)
    let encodedURLRequest = try? URLEncoding.queryString.encode(urlRequest, with: parameters)
    return encodedURLRequest?.url?.absoluteString ?? "" // FIXME: this should not be nil.
}

// MARK: - Login
public extension DiscuzClient {
    /**
     A check request should be sent to a discuz! server to make sure whether a seccode is necessary for login.

     - parameter noSechashBlock:  Executed if seccode is disabled on this server.
     - parameter hasSeccodeBlock: Executed if seccode is enabled on this server.
     - parameter failureBlock:    Executed if this request failed.

     - returns: Request object.
     */
    @discardableResult
    public func checkLoginType(noSechashBlock: @escaping () -> Void,
                               hasSeccodeBlock: @escaping (_ sechash: String) -> Void,
                               failureBlock: @escaping (_ error: Error) -> Void) -> Request {
        logOut()
        let parameters: Parameters = [
            "module": "secure",
            "version": 1,
            "mobile": "no",
            "type": "login",
        ]
        return Alamofire.request(baseURL + "/api/mobile/index.php", parameters: parameters).responseSwiftyJSON { response in
            debugPrint(response.request as Any)
            switch response.result {
            case let .success(json):
                if let sechash = json["Variables"]["sechash"].string {
                    hasSeccodeBlock(sechash)
                } else {
                    noSechashBlock()
                }
            case let .failure(error):
                failureBlock(error)
            }
        }
    }

    public enum AuthMode {
        case basic
        case secure(hash: String, code: String)
    }

    /**
     Request to login when seccode is not necessary.

     - parameter username:             Username of account.
     - parameter password:             <PASSWORD> account.
     - parameter secureQuestionNumber: Secure question number of account. This should be set to 0 if no question is setted.
     - parameter secureQuestionAnswer: Answer of secure question of account.
     - parameter authMode:             Auth mode for log in.
     - parameter successBlock:         Executed if login request finished without network error.
     - parameter failureBlock:         Executed if login request not finished due to network error.

     - returns: Request object.
     */
    @discardableResult
    public func logIn(username: String, password: String, secureQuestionNumber: Int, secureQuestionAnswer: String, authMode: AuthMode, completion: @escaping (Result<String?>) -> Void) -> Request {
        var URLParameters: Parameters = [
            "module": "login",
            "version": 1,
            "loginsubmit": "yes",
            "loginfield": "auto",
            "cookietime": 2_592_000,
            "mobile": "no",
        ]

        if case let .secure(hash, code) = authMode {
            URLParameters["sechash"] = hash
            URLParameters["seccodeverify"] = code
        }
        let URLString = generateURLString(baseURL + "/api/mobile/", parameters: URLParameters)

        let bodyParameters: Parameters = [
            "username": username,
            "password": password,
            "questionid": secureQuestionNumber,
            "answer": secureQuestionAnswer,
        ]

        return Alamofire.request(URLString, method: .post, parameters: bodyParameters).responseSwiftyJSON { response in
            debugPrint(response.request as Any)
            switch response.result {
            case let .success(json):
                if let messageValue = json["Message"]["messageval"].string, messageValue.contains("login_succeed") {
                    UserDefaults.standard.set(username, forKey: "InLoginStateID")
                    NotificationCenter.default.post(name: .DZLoginStatusDidChangeNotification, object: nil)
                    completion(.success(json["Message"]["messagestr"].string))
                } else {
                    completion(.failure(DZError.loginFailed(messageValue: json["Message"]["messageval"].stringValue, messageString: json["Message"]["messagestr"].stringValue)))
                }
            case let .failure(error):
                completion(.failure(error))
            }
        }
    }

    @discardableResult
    func getSeccodeImage(sechash: String, completion: @escaping (Result<UIImage>) -> Void) -> Request {
        let parameters: Parameters = ["module": "seccode", "version": 1, "mobile": "no", "sechash": sechash]
        return Alamofire.request(baseURL + "/api/mobile/index.php", method: .get, parameters: parameters, encoding: URLEncoding.queryString, headers: nil).responseImage { response in
            switch response.result {
            case let .success(image):<commit_msg>Add error recorder to catch information about login failure.<commit_after>//
//  DiscuzClient+Teleport.swift
//  Stage1st
//
//  Created by <NAME> on 5/24/16.
//  Copyright © 2016 Renaissance. All rights reserved.
//

import Foundation
import Alamofire
import SwiftyJSON
import Crashlytics

private func generateURLString(_ baseURLString: String, parameters: Parameters) -> String {
    let urlRequest = URLRequest(url: URL(string: baseURLString)!)
    let encodedURLRequest = try? URLEncoding.queryString.encode(urlRequest, with: parameters)
    return encodedURLRequest?.url?.absoluteString ?? "" // FIXME: this should not be nil.
}

// MARK: - Login
public extension DiscuzClient {
    /**
     A check request should be sent to a discuz! server to make sure whether a seccode is necessary for login.

     - parameter noSechashBlock:  Executed if seccode is disabled on this server.
     - parameter hasSeccodeBlock: Executed if seccode is enabled on this server.
     - parameter failureBlock:    Executed if this request failed.

     - returns: Request object.
     */
    @discardableResult
    public func checkLoginType(noSechashBlock: @escaping () -> Void,
                               hasSeccodeBlock: @escaping (_ sechash: String) -> Void,
                               failureBlock: @escaping (_ error: Error) -> Void) -> Request {
        logOut()
        let parameters: Parameters = [
            "module": "secure",
            "version": 1,
            "mobile": "no",
            "type": "login",
        ]
        return Alamofire.request(baseURL + "/api/mobile/index.php", parameters: parameters).responseSwiftyJSON { response in
            debugPrint(response.request as Any)
            switch response.result {
            case let .success(json):
                if let sechash = json["Variables"]["sechash"].string {
                    hasSeccodeBlock(sechash)
                } else {
                    noSechashBlock()
                }
            case let .failure(error):
                failureBlock(error)
            }
        }
    }

    public enum AuthMode {
        case basic
        case secure(hash: String, code: String)
    }

    /**
     Request to login when seccode is not necessary.

     - parameter username:             Username of account.
     - parameter password:             <PASSWORD>.
     - parameter secureQuestionNumber: Secure question number of account. This should be set to 0 if no question is setted.
     - parameter secureQuestionAnswer: Answer of secure question of account.
     - parameter authMode:             Auth mode for log in.
     - parameter successBlock:         Executed if login request finished without network error.
     - parameter failureBlock:         Executed if login request not finished due to network error.

     - returns: Request object.
     */
    @discardableResult
    public func logIn(username: String, password: String, secureQuestionNumber: Int, secureQuestionAnswer: String, authMode: AuthMode, completion: @escaping (Result<String?>) -> Void) -> Request {
        var URLParameters: Parameters = [
            "module": "login",
            "version": 1,
            "loginsubmit": "yes",
            "loginfield": "auto",
            "cookietime": 2_592_000,
            "mobile": "no",
        ]

        if case let .secure(hash, code) = authMode {
            URLParameters["sechash"] = hash
            URLParameters["seccodeverify"] = code
        }
        let URLString = generateURLString(baseURL + "/api/mobile/", parameters: URLParameters)

        let bodyParameters: Parameters = [
            "username": username,
            "password": password,
            "questionid": secureQuestionNumber,
            "answer": secureQuestionAnswer,
        ]

        var debugHeader: String? = nil
        var debugRequestMethod: String? = nil
        var debugURLString: String? = nil
        var debugMIMEType: String? = nil
        var debugBodyString: String? = nil

        return Alamofire.request(URLString, method: .post, parameters: bodyParameters).responseString(completionHandler: { (response) in
            debugRequestMethod = response.request?.httpMethod
            debugHeader = response.response?.description
            debugURLString = response.response?.url?.absoluteString
            debugMIMEType = response.response?.mimeType
            debugBodyString = response.value
        }).responseSwiftyJSON { response in
            debugPrint(response.request as Any)
            switch response.result {
            case let .success(json):
                if let messageValue = json["Message"]["messageval"].string, messageValue.contains("login_succeed") {
                    UserDefaults.standard.set(username, forKey: "InLoginStateID")
                    NotificationCenter.default.post(name: .DZLoginStatusDidChangeNotification, object: nil)
                    completion(.success(json["Message"]["messagestr"].string))
                } else {
                    completion(.failure(DZError.loginFailed(messageValue: json["Message"]["messageval"].stringValue, messageString: json["Message"]["messagestr"].stringValue)))
                }
            case let .failure(error):
                if case AFError.responseSerializationFailed = error {
                    let userInfo = [
                        "method": debugRequestMethod ?? "",
                        "header": debugHeader ?? "",
                        "url": debugURLString ?? "",
                        "MIME": debugMIMEType ?? "",
                        "body": debugBodyString ?? ""
                    ]
                    let recoredError = NSError(domain: "LoginSerializationFailed", code: 0, userInfo: userInfo)
                    Crashlytics.sharedInstance().recordError(recoredError)
                }
                completion(.failure(error))
            }
        }
    }

    @discardableResult
    func getSeccodeImage(sechash: String, completion: @escaping (Result<UIImage>) -> Void) -> Request {
        let parameters: Parameters = ["module": "seccode", "version": 1, "mobile": "no", "sechash": sechash]
        return Alamofire.request(baseURL + "/api/mobile/index.php", method: .get, parameters: parameters, encoding: URLEncoding.queryString, headers: nil).responseImage { response in
            switch response.result {
            case let .success(image):
{'fraction_non_alphanumeric': 0.09894664134001036, 'fraction_numerical': 0.0037126575720946298, 'mean_word_length': 2.1944291230005515, 'pattern_counts': {'":': 41, '<': 11, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1991911', 'n_tokens_mistral': 3008, 'n_tokens_neox': 2884, 'n_words': 824}