//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Amplify
import AWSPinpoint
import Foundation

@_spi(InternalAWSPinpoint)
public class PinpointEndpointProfile: Codable {
    typealias DeviceToken = String

    var applicationId: String
    var endpointId: String
    var deviceToken: DeviceToken?
    var effectiveDate: Date
    var isDebug: Bool
    var isOptOut: Bool
    var location: PinpointClientTypes.EndpointLocation
    var demographic: PinpointClientTypes.EndpointDemographic
    private(set) var user: PinpointClientTypes.EndpointUser
    private(set) var attributes: [String: [String]] = [:]
    private(set) var metrics: [String: Double] = [:]

    init(applicationId: String,
         endpointId: String,
         deviceToken: DeviceToken? = nil,
         effectiveDate: Date = Date(),
         isDebug: Bool = false,
         isOptOut: Bool = false,
         location: PinpointClientTypes.EndpointLocation = .init(),
         demographic: PinpointClientTypes.EndpointDemographic = .init(),
         user: PinpointClientTypes.EndpointUser = .init()) {
        self.applicationId = applicationId
        self.endpointId = endpointId
        self.deviceToken = deviceToken
        self.effectiveDate = effectiveDate
        self.isDebug = isDebug
        self.isOptOut = isOptOut
        self.location = location
        self.demographic = demographic
        self.user = user
    }

    public func addUserId(_ userId: String) {
        user.userId = userId
    }

    public func addUserProfile(_ userProfile: UserProfile) {
        if let email = userProfile.email {
            setCustomProperty(email, forKey: Constants.AttributeKeys.email)
        }

        if let name = userProfile.name {
            setCustomProperty(name, forKey: Constants.AttributeKeys.name)
        }

        if let plan = userProfile.plan {
            setCustomProperty(plan, forKey: Constants.AttributeKeys.plan)
        }

        addCustomProperties(userProfile.customProperties)
        if let pinpointUser = userProfile as? PinpointUserProfile {
            addUserAttributes(pinpointUser.userAttributes)
            if let optedOutOfMessages = pinpointUser.optedOutOfMessages {
                isOptOut = optedOutOfMessages
            }
        }

        if let userLocation = userProfile.location {
            location.update(with: userLocation)
        }
    }

    public func setAPNsToken(_ apnsToken: Data) {
        deviceToken = apnsToken.asHexString()
    }

    private func addCustomProperties(_ properties: [String: UserProfilePropertyValue]?) {
        guard let properties = properties else { return }
        for (key, value) in properties {
            setCustomProperty(value, forKey: key)
        }
    }

    private func addUserAttributes(_ attributes: [String: [String]]?) {
        guard let attributes = attributes else { return }
        let userAttributes = user.userAttributes ?? [:]
        user.userAttributes = userAttributes.merging(
            attributes,
            uniquingKeysWith: { _, new in new }
        )
    }

    private func setCustomProperty(_ value: UserProfilePropertyValue,
                                   forKey key: String) {
        if let value = value as? String {
            attributes[key] = [value]
        } else if let values = value as? [String] {
            attributes[key] = values
        } else if let value = value as? Bool {
            attributes[key] = [String(value)]
        } else if let value = value as? Int {
            metrics[key] = Double(value)
        } else if let value = value as? Double {
            metrics[key] = value
        }
    }
}

extension Optional where Wrapped == PinpointEndpointProfile.DeviceToken {
    var isNotEmpty: Bool {
        guard let self = self else { return false }
        return !self.isEmpty
    }
}

extension PinpointEndpointProfile {
    struct Constants {
        struct AttributeKeys {
            static let email = "email"
            static let name = "name"
            static let plan = "plan"
        }
    }
}