/*
Copyright 2020-2024 New Vector Ltd.

SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
 */

import Foundation

enum CrossSigningServiceError: Int, Error {
    case authenticationRequired
    case unknown
}

extension CrossSigningServiceError: CustomNSError {
    public static let errorDomain = "CrossSigningService"

    public var errorCode: Int {
        return Int(rawValue)
    }

    public var errorUserInfo: [String: Any] {
        return [:]
    }
}

@objcMembers
final class CrossSigningService: NSObject {
    
    // MARK - Properties
    
    private var supportSetupKeyVerificationByUser: [String: Bool] = [:] // Cached server response
    private var userInteractiveAuthenticationService: UserInteractiveAuthenticationService?
    
    // MARK - Public
    
    @discardableResult
    func canSetupCrossSigning(for session: MXSession, success: @escaping ((Bool) -> Void), failure: @escaping ((Error) -> Void)) -> MXHTTPOperation? {
        
        guard let crossSigning = session.crypto?.crossSigning, crossSigning.state == .notBootstrapped else {
            // Cross-signing already setup
            success(false)
            return nil
        }
        
        let userId: String = session.myUserId
        
        if let supportSetupKeyVerification = self.supportSetupKeyVerificationByUser[userId] {
            // Return cached response
            success(supportSetupKeyVerification)
            return nil
        }
        
        let userInteractiveAuthenticationService = UserInteractiveAuthenticationService(session: session)
        
        self.userInteractiveAuthenticationService = userInteractiveAuthenticationService
                
        let request = self.setupCrossSigningRequest()
        
        return userInteractiveAuthenticationService.canAuthenticate(with: request) { (result) in
            switch result {
            case .success(let succeeded):
                success(succeeded)
            case .failure(let error):
                failure(error)
            }
        }
    }
    
    func setupCrossSigningRequest() -> AuthenticatedEndpointRequest {
        // bwi 7295 could be fixed here to by adding a fake master key to the request
        let path = "\(kMXAPIPrefixPathUnstable)/keys/device_signing/upload"
        return AuthenticatedEndpointRequest(path: path, httpMethod: "POST", params: [:])
    }
    
    /// Setup cross-signing without authentication. Useful when a grace period is enabled.
    @discardableResult
    func setupCrossSigningWithoutAuthentication(for session: MXSession, success: @escaping (() -> Void), failure: @escaping ((Error) -> Void)) -> MXHTTPOperation? {
        
        guard let crossSigning = session.crypto?.crossSigning else {
            failure(CrossSigningServiceError.unknown)
            return nil
        }
        
        let userInteractiveAuthenticationService = UserInteractiveAuthenticationService(session: session)
        self.userInteractiveAuthenticationService = userInteractiveAuthenticationService
                        
        let request = self.setupCrossSigningRequest()
        
        return userInteractiveAuthenticationService.authenticatedEndpointStatus(for: request) { result in
            switch result {
            case .success(let authenticatedEnpointStatus):
                switch authenticatedEnpointStatus {
                case .authenticationNeeded:
                    failure(CrossSigningServiceError.authenticationRequired)
                case .authenticationNotNeeded:
                    crossSigning.setup(withAuthParams: [:]) {
                        success()
                    } failure: { error in
                        failure(error)
                    }
                }
            case .failure(let error):
                failure(error)
            }
        }
    }
}
