<commit_before>//
//  YSCredentialManager.swift
//  YSGGP
//
//  Created by <NAME> on 11/16/16.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import Foundation
import GoogleSignIn
import Firebase
import SwiftMessages
import KeychainAccess
import ReachabilitySwift

typealias AccessTokenAddedCompletionHandler = (_ request: URLRequest, _ error: YSErrorProtocol?) -> Swift.Void

class YSCredentialManager
{
    static let shared : YSCredentialManager =
    {
        let instance = YSCredentialManager()
        return instance
    }()
    
    private init()
    {
        let keychain = Keychain(service: YSConstants.kTokenKeychainKey)
        let tokenData = keychain[data: YSConstants.kTokenKeychainItemKey]
        if tokenData == nil
        {
            return
        }
        if (tokenData?.isEmpty)!
        {
            return
        }
        let tokenDictionary = NSKeyedUnarchiver.unarchiveObject(with: tokenData!) as! [String : Any]
        self.token = tokenDictionary.toYSToken()
    }
    
    private var token : YSToken = YSToken()
    
    private var isValidAccessToken : Bool
    {
        let isTokenPresent = !token.accessToken.isEmpty
        let isNotTimedOut = token.accessTokenAvailableTo.isLessThanDate(date: Date())
        return isTokenPresent && !isNotTimedOut
    }
    
    func isPresentRefreshToken() -> Bool
    {
        return !token.refreshToken.isEmpty
    }
    
    func setTokens(refreshToken : String, accessToken : String, availableTo : Date)
    {
        token.refreshToken = refreshToken
        token.accessToken = accessToken
        token.accessTokenAvailableTo = availableTo
        saveTokenToKeychain()
    }
    
    func setAccessToken(tokenType : String, accessToken : String, availableTo : Date)
    {
        token.accessTokenTokenType = tokenType
        token.accessToken = accessToken
        token.accessTokenAvailableTo = availableTo
        saveTokenToKeychain()
    }
    
    private func saveTokenToKeychain()
    {
        let keychain = Keychain(service: YSConstants.kTokenKeychainKey)
        let tokenDictionary = token.toDictionary()
        let tokenData = NSKeyedArchiver.archivedData(withRootObject: tokenDictionary)
        keychain[data: YSConstants.kTokenKeychainItemKey] = tokenData
    }
    
    private func urlForAccessToken() -> URL
    {
        let url = "\(YSConstants.kAccessTokenAPIEndpoint)?client_id=\(YSConstants.kDriveClientID)&refresh_token=\(token.refreshToken)&grant_type=refresh_token"
        return URL.init(string: url)!
    }
    
    func addAccessTokenHeaders(_ request: URLRequest, _ completionHandler: @escaping AccessTokenAddedCompletionHandler)
    {
        if !isValidAccessToken
        {
            var requestForToken = URLRequest.init(url: urlForAccessToken())
            requestForToken.httpMethod = "POST"
            
            let task = Foundation.URLSession.shared.dataTask(with: requestForToken)
            { data, response, error in
                
                if let err = YSNetworkResponseManager.validate(response, error: error)
                {
                    completionHandler(request, err)
                    return
                }
                let dict = YSNetworkResponseManager.convertToDictionary(from: data!)
                if let accessToken = dict["access_token"] as? String , let tokenType = dict["token_type"] as? String, let expiresIn = dict["expires_in"] as? NSNumber
                {
                    let availableTo = Date().addingTimeInterval(expiresIn.doubleValue)
                    self.setAccessToken(tokenType: tokenType, accessToken: accessToken, availableTo: availableTo)
                    self.addHeaders(to: request, completionHandler)
                }
            }
            task.resume()
            return
        }
        addHeaders(to: request, completionHandler)
    }
    
    private func addHeaders(to request: URLRequest, _ completionHandler: @escaping AccessTokenAddedCompletionHandler)
    {
        var request = request
        if token.accessTokenTokenType.isEmpty
        {
            request.setValue("Bearer \(token.accessToken)", forHTTPHeaderField: "Authorization")
            print("Request URL:  \(String(describing: request.url))   Authorization:  Bearer \(token.accessToken)")
            completionHandler(request, nil)
        }
        else
        {
            request.setValue("\(token.accessTokenTokenType) \(token.accessToken)", forHTTPHeaderField: "Authorization")<commit_msg>Better readability<commit_after>//
//  YSCredentialManager.swift
//  YSGGP
//
//  Created by <NAME> on 11/16/16.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import Foundation
import GoogleSignIn
import Firebase
import SwiftMessages
import KeychainAccess
import ReachabilitySwift

typealias AccessTokenAddedCompletionHandler = (_ request: URLRequest, _ error: YSErrorProtocol?) -> Swift.Void

class YSCredentialManager
{
    static let shared : YSCredentialManager =
    {
        let instance = YSCredentialManager()
        return instance
    }()
    
    private init()
    {
        let keychain = Keychain(service: YSConstants.kTokenKeychainKey)
        let tokenData = keychain[data: YSConstants.kTokenKeychainItemKey]
        if tokenData == nil
        {
            return
        }
        if (tokenData?.isEmpty)!
        {
            return
        }
        let tokenDictionary = NSKeyedUnarchiver.unarchiveObject(with: tokenData!) as! [String : Any]
        self.token = tokenDictionary.toYSToken()
    }
    
    private var token : YSToken = YSToken()
    
    private var isValidAccessToken : Bool
    {
        let isTokenPresent = !token.accessToken.isEmpty
        let isNotTimedOut = token.accessTokenAvailableTo.isLessThanDate(date: Date())
        return isTokenPresent && !isNotTimedOut
    }
    
    func isPresentRefreshToken() -> Bool
    {
        return !token.refreshToken.isEmpty
    }
    
    func setTokens(refreshToken : String, accessToken : String, availableTo : Date)
    {
        token.refreshToken = refreshToken
        token.accessToken = accessToken
        token.accessTokenAvailableTo = availableTo
        saveTokenToKeychain()
    }
    
    func setAccessToken(tokenType : String, accessToken : String, availableTo : Date)
    {
        token.accessTokenTokenType = tokenType
        token.accessToken = accessToken
        token.accessTokenAvailableTo = availableTo
        saveTokenToKeychain()
    }
    
    private func saveTokenToKeychain()
    {
        let keychain = Keychain(service: YSConstants.kTokenKeychainKey)
        let tokenDictionary = token.toDictionary()
        let tokenData = NSKeyedArchiver.archivedData(withRootObject: tokenDictionary)
        keychain[data: YSConstants.kTokenKeychainItemKey] = tokenData
    }
    
    private func urlForAccessToken() -> URL
    {
        let url = "\(YSConstants.kAccessTokenAPIEndpoint)?client_id=\(YSConstants.kDriveClientID)&refresh_token=\(token.refreshToken)&grant_type=refresh_token"
        return URL.init(string: url)!
    }
    
    func addAccessTokenHeaders(_ request: URLRequest, _ completionHandler: @escaping AccessTokenAddedCompletionHandler)
    {
        if isValidAccessToken
        {
            addHeaders(to: request, completionHandler)
        }
        var requestForToken = URLRequest.init(url: urlForAccessToken())
        requestForToken.httpMethod = "POST"
        
        let task = Foundation.URLSession.shared.dataTask(with: requestForToken)
        { data, response, error in
            
            if let err = YSNetworkResponseManager.validate(response, error: error)
            {
                completionHandler(request, err)
                return
            }
            let dict = YSNetworkResponseManager.convertToDictionary(from: data!)
            if let accessToken = dict["access_token"] as? String , let tokenType = dict["token_type"] as? String, let expiresIn = dict["expires_in"] as? NSNumber
            {
                let availableTo = Date().addingTimeInterval(expiresIn.doubleValue)
                self.setAccessToken(tokenType: tokenType, accessToken: accessToken, availableTo: availableTo)
                self.addHeaders(to: request, completionHandler)
            }
        }
        task.resume()
    }
    
    private func addHeaders(to request: URLRequest, _ completionHandler: @escaping AccessTokenAddedCompletionHandler)
    {
        var request = request
        if token.accessTokenTokenType.isEmpty
        {
            request.setValue("Bearer \(token.accessToken)", forHTTPHeaderField: "Authorization")
            print("Request URL:  \(String(describing: request.url))   Authorization:  Bearer \(token.accessToken)")
            completionHandler(request, nil)
        }
        else
        {
            request.setValue("\(token.accessTokenTokenType) \(token.accessToken)", forHTTPHeaderField: "Authorization")
{'fraction_non_alphanumeric': 0.08276402732668832, 'fraction_numerical': 0.0022398924851607122, 'mean_word_length': 2.5005880047040376, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, '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': '2257724', 'n_tokens_mistral': 2400, 'n_tokens_neox': 2346, 'n_words': 499}