//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation

public protocol OWSSignalServiceProtocol: AnyObject {
    func warmCaches()

    // MARK: - Censorship Circumvention

    var isCensorshipCircumventionActive: Bool { get }
    var hasCensoredPhoneNumber: Bool { get }
    var isCensorshipCircumventionManuallyActivated: Bool { get set }
    var isCensorshipCircumventionManuallyDisabled: Bool { get set }
    var manualCensorshipCircumventionCountryCode: String? { get set }

    func updateHasCensoredPhoneNumberDuringProvisioning(_ e164: E164)

    func buildUrlEndpoint(for signalServiceInfo: SignalServiceInfo) -> OWSURLSessionEndpoint
    func buildUrlSession(
        for signalServiceInfo: SignalServiceInfo,
        endpoint: OWSURLSessionEndpoint,
        configuration: URLSessionConfiguration?
    ) -> OWSURLSessionProtocol
}

public enum SignalServiceType {
    case mainSignalServiceIdentified
    case mainSignalServiceUnidentified
    case storageService
    case cdn0
    case cdn2
    case cdn3
    case updates
    case updates2
    case contactDiscoveryV2
    case svr2

    static func type(forCdnNumber cdnNumber: UInt32) -> SignalServiceType {
        switch cdnNumber {
        case 0:
            return cdn0
        case 2:
            return cdn2
        case 3:
            return cdn3
        default:
            owsFailDebug("Unrecognized CDN number configuration requested: \(cdnNumber)")
            return cdn2
        }
    }
}

// MARK: -

public extension OWSSignalServiceProtocol {

    private func buildUrlSession(for signalServiceType: SignalServiceType) -> OWSURLSessionProtocol {
        let signalServiceInfo = signalServiceType.signalServiceInfo()
        return buildUrlSession(
            for: signalServiceInfo,
            endpoint: buildUrlEndpoint(for: signalServiceInfo),
            configuration: nil
        )
    }

    func urlSessionForMainSignalService() -> OWSURLSessionProtocol {
        buildUrlSession(for: .mainSignalServiceIdentified)
    }

    func urlSessionForStorageService() -> OWSURLSessionProtocol {
        buildUrlSession(for: .storageService)
    }

    func urlSessionForCdn(cdnNumber: UInt32) -> OWSURLSessionProtocol {
        buildUrlSession(for: SignalServiceType.type(forCdnNumber: cdnNumber))
    }

    func urlSessionForUpdates() -> OWSURLSessionProtocol {
        buildUrlSession(for: .updates)
    }

    func urlSessionForUpdates2() -> OWSURLSessionProtocol {
        buildUrlSession(for: .updates2)
    }
}

// MARK: - Service type mapping

public struct SignalServiceInfo {
    let baseUrl: URL
    let censorshipCircumventionPathPrefix: String
    let shouldUseSignalCertificate: Bool
    let shouldHandleRemoteDeprecation: Bool
}

extension SignalServiceType {

    public func signalServiceInfo() -> SignalServiceInfo {
        switch self {
        case .mainSignalServiceIdentified:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.mainServiceIdentifiedURL)!,
                censorshipCircumventionPathPrefix: TSConstants.serviceCensorshipPrefix,
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: true
            )
        case .mainSignalServiceUnidentified:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.mainServiceUnidentifiedURL)!,
                censorshipCircumventionPathPrefix: TSConstants.serviceCensorshipPrefix,
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: true
            )
        case .storageService:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.storageServiceURL)!,
                censorshipCircumventionPathPrefix: TSConstants.storageServiceCensorshipPrefix,
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: true
            )
        case .cdn0:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.textSecureCDN0ServerURL)!,
                censorshipCircumventionPathPrefix: TSConstants.cdn0CensorshipPrefix,
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: false
            )
        case .cdn2:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.textSecureCDN2ServerURL)!,
                censorshipCircumventionPathPrefix: TSConstants.cdn2CensorshipPrefix,
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: false
            )
        case .cdn3:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.textSecureCDN3ServerURL)!,
                censorshipCircumventionPathPrefix: TSConstants.cdn3CensorshipPrefix,
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: false
            )
        case .updates:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.updatesURL)!,
                censorshipCircumventionPathPrefix: "unimplemented",
                shouldUseSignalCertificate: false,
                shouldHandleRemoteDeprecation: false
            )
        case .updates2:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.updates2URL)!,
                censorshipCircumventionPathPrefix: "unimplemented", // BADGES TODO
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: false
            )
        case .contactDiscoveryV2:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.contactDiscoveryV2URL)!,
                censorshipCircumventionPathPrefix: TSConstants.contactDiscoveryV2CensorshipPrefix,
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: false
            )
        case .svr2:
            return SignalServiceInfo(
                baseUrl: URL(string: TSConstants.svr2URL)!,
                censorshipCircumventionPathPrefix: TSConstants.svr2CensorshipPrefix,
                shouldUseSignalCertificate: true,
                shouldHandleRemoteDeprecation: false
            )
        }
    }
}
