//
//  FirebaseAuthPresenter.swift
//  NearbyStoresPro
//
//  Created by DT Team maagoul on 15/1/2023.
//  Copyright © 2023 Amine. All rights reserved.
//

import Foundation


import FirebaseAuth
import GoogleSignIn
import FBSDKLoginKit
import AuthenticationServices
import FirebaseCore
import FirebaseAuth
import GoogleSignIn
import FBSDKLoginKit
import CommonCrypto
import Firebase


protocol FirebaseAuthProtocol {
    func signInWithFacebook()
    func signInWithGoogle()
    func signInWithApple()
    func signOut()
}


class FirebaseAuthPresenter: NSObject, FirebaseAuthProtocol, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
    
    
    @available(iOS 13.0, *)
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return (self.delegate! as! MyUIViewController).view.window!
    }

    var delegate: FirebaseAuthViewProtocol?
    
    override init() {
        
        if Session.isLogged(){
            return
        }
        
        let firebaseAuth = Auth.auth()
         do {
             try firebaseAuth.signOut()
         } catch let signOutError as NSError {
             print("Error signing out: %@", signOutError)
         }
        
    }
    
    func signInWithGoogle() {
        
        
        guard let clientID = FirebaseApp.app()?.options.clientID else { return }
      
        let config = GIDConfiguration(clientID: clientID)

        GIDSignIn.sharedInstance.configuration = config
                
        GIDSignIn.sharedInstance.signIn(withPresenting: (self.delegate! as! MyUIViewController)) { signResult, error in
                    
            if let error = error {
                self.delegate?.onFireAuthError(error: error.localizedDescription)
               return
            }
       
             guard let user = signResult?.user,
                   let idToken = user.idToken else { return }
             
             let accessToken = user.accessToken
                    
             let credential = GoogleAuthProvider.credential(withIDToken: idToken.tokenString, accessToken: accessToken.tokenString)

            // Use the credential to authenticate with Firebase
            
            
            self.delegate?.onFireAuthPreload()

            Auth.auth().signIn(with: credential) { (authResult, error) in
                                      
                if let error1 = error {
                    self.delegate?.onFireAuthError(error: error1.localizedDescription)
                    return
                }
                     
                
                guard let result = authResult else{
                    self.delegate?.onFireAuthError(error: "Error#02")
                    return
                }
                
                
                Utils.printDebug("\(result)")
                
                var photoURL = ""
                if let photo = result.user.photoURL{
                    photoURL = photo.absoluteString
                }
                
                // User is now logged in
                let faResult  = FAResult(token: accessToken.tokenString, uniqueid:  result.user.uid,
                                         username: result.user.uid,
                                         email: result.user.email!,
                                         name: result.user.displayName!, avatar: photoURL,
                                    source: .google)
                
                self.delegate?.onFireAuthSuccess(result: faResult)
                        
            }
        }
       
        
    }
    

    fileprivate var currentNonce: String?
    
   
    func signInWithApple() {
        
        if #available(iOS 13.0, *) {
            
            let nonce = randomNonceString()
            currentNonce = nonce
            let appleIDProvider = ASAuthorizationAppleIDProvider()
            let request = appleIDProvider.createRequest()
            request.requestedScopes = [.fullName, .email]
            request.nonce = sha256(nonce)

            let authorizationController = ASAuthorizationController(authorizationRequests: [request])
            authorizationController.delegate = self
            authorizationController.presentationContextProvider = self
            authorizationController.performRequests()
            
        }else{
            self.delegate?.onFireAuthError(error: "Apple login iOS version doesn't work with less than ios 13")
        }
        
        
        
    }
    
    
    @available(iOS 13.0, *)
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
          guard let nonce = currentNonce else {
              self.delegate?.onFireAuthError(error: "Invalid state: A login callback was received, but no login request was sent.")
              fatalError("Invalid state: A login callback was received, but no login request was sent.")
          }
            
          guard let appleIDToken = appleIDCredential.identityToken else {
              self.delegate?.onFireAuthError(error: "Unable to fetch identity token")
            return
          }
          guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
              self.delegate?.onFireAuthError(error: "Unable to serialize token string from data: \(appleIDToken.debugDescription)")
            return
          }
          // Initialize a Firebase credential.
          let credential = OAuthProvider.credential(withProviderID: "apple.com",
                                                    idToken: idTokenString,
                                                    rawNonce: nonce)
            
            self.delegate?.onFireAuthPreload()
            
          // Sign in with Firebase.
          Auth.auth().signIn(with: credential) { (authResult, error) in
              
              if let error1 = error {
                  self.delegate?.onFireAuthError(error: error1.localizedDescription)
                  return
              }
              
              guard let result = authResult else{
                  self.delegate?.onFireAuthError(error: "Error#02")
                  return
              }
              
              Utils.printDebug("\(result)")
              
              var displayName = "Apple User"
              
              if let name = result.user.displayName{
                displayName = name
              }
              
              var photoURL = ""
              if let photo = result.user.photoURL{
                  photoURL = photo.absoluteString
              }
              
              // User is now logged in
              let faResult  = FAResult(token: idTokenString,
                                       uniqueid:  result.user.uid,
                                       username: result.user.uid,
                                       email: result.user.email!,
                                       name: displayName,
                                       avatar: photoURL,
                                  source: .apple)
              
              self.delegate?.onFireAuthSuccess(result: faResult)
            
          }
        }
      }

    @available(iOS 13.0, *)
    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        // Handle error.
        print("Sign in with Apple errored: \(error)")
        
      }

    func signOut() {
        
        
        
    }
    
    
    func signInWithFacebook() {
        
        let controller = (self.delegate! as! MyUIViewController)
        
        let loginManager = LoginManager()
        loginManager.logIn(permissions: ["public_profile", "email"], from: controller) { (result, error) in
             
            if let error = error {
                  self.delegate?.onFireAuthError(error: error.localizedDescription)
                  return
              }

              guard let accessToken = AccessToken.current else {
                  self.delegate?.onFireAuthError(error: "Failed to get access token")
                  return
              }

            self.delegate?.onFireAuthPreload()
            
              let credential = FacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)
              Auth.auth().signIn(with: credential) { (authResult, error) in
                  if let error = error {
                      self.delegate?.onFireAuthError(error: error.localizedDescription)
                      return
                  }
                  
                  
                  guard let result = authResult else{
                      self.delegate?.onFireAuthError(error: "Error#01")
                      return
                  }
                  
                  var photoURL = ""
                  if let photo = result.user.photoURL{
                      photoURL = photo.absoluteString
                  }
                  
                  Utils.printDebug("\(result)")
                  
                  // User is now logged in
                  let faResult  = FAResult(token: accessToken.tokenString, uniqueid:  result.user.uid,
                                           username: result.user.uid,
                                           email: result.user.email!,
                                           name: result.user.displayName!,
                                           avatar: photoURL,
                                      source: .facebook)
                  
                  self.delegate?.onFireAuthSuccess(result: faResult)
              }
          }
      }
    

    
    private func randomNonceString(length: Int = 32) -> String {
    
      precondition(length > 0)
      let charset: [Character] =
        Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
      var result = ""
      var remainingLength = length

      while remainingLength > 0 {
        let randoms: [UInt8] = (0 ..< 16).map { _ in
          var random: UInt8 = 0
          let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
          if errorCode != errSecSuccess {
            fatalError(
              "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
            )
          }
          return random
        }

        randoms.forEach { random in
          if remainingLength == 0 {
            return
          }

          if random < charset.count {
            result.append(charset[Int(random)])
            remainingLength -= 1
          }
        }
      }

      return result
    }
    

    private func sha256(_ input: String) -> String {
        let inputData = input.data(using: .utf8)!
        var hashedData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
        _ = hashedData.withUnsafeMutableBytes { hashedBytes in
            inputData.withUnsafeBytes { inputBytes in
                CC_SHA256(inputBytes, CC_LONG(inputData.count), hashedBytes)
            }
        }
        return hashedData.map { String(format: "%02x", $0) }.joined()
    }
    
}

struct FAResult {
    var token: String
    var uniqueid: String
    var username:String
    var email:String
    var name:String
    var avatar:String
    var source:FAResultSource
}

enum FAResultSource {
    case facebook
    case google
    case apple
}
