//
//  File.swift
//  react-native-firework-sdk
//
//  Created by Jeff Zheng on 2021/12/30.
//

// swiftlint:disable file_length
import FireworkVideo
import UIKit

@objc(ShoppingModule)
// swiftlint:disable:next type_body_length
class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate {

    private var productInfoViewConfigurator: (Int, ProductInfoViewConfigurable)?
    private var shoppingCTAHandlerMap: [Int: ShoppingCTAHandler] = [:]
    private var productHydratingMap: [Int: ProductHydrating] = [:]

    private var cartIconVisible: Bool?
    private var itemCounts = 0
    private var productInfoViewConfiguration: ProductInfoViewConfiguration?
    private var customClickLinkButtonEnabled = false
    private var customTapProductCardEnabled = false
    private var playerMap: NSMapTable<NSNumber, UIViewController> = NSMapTable.strongToWeakObjects()

    override func supportedEvents() -> [String]! {
        ShoppingEventName.allCases.map { $0.rawValue }
    }

    override class func requiresMainQueueSetup() -> Bool {
        return true
    }

    override var methodQueue: DispatchQueue {
        return DispatchQueue.main
    }

    private static func generateCallbackId() -> Int {
        struct CBIdIncreasingFactor {
            static var callbackId = 0
        }

        CBIdIncreasingFactor.callbackId += 1
        return CBIdIncreasingFactor.callbackId
    }

    @objc(initializeSDK:rejecter:)
    func initializeSDK(_ resolver: RCTPromiseResolveBlock,
                       rejecter: RCTPromiseRejectBlock) {
        FireworkVideoSDK.shopping.shoppingDelegate = self
        FireworkVideoSDK.shopping.cartAction = .custom
        resolver([:])
    }

    @objc
    func setCartIconVisible(_ visible: Bool) {
        cartIconVisible = visible
        guard let rProductInfoViewConfigurator = productInfoViewConfigurator else {
            return
        }

        rProductInfoViewConfigurator.1.shoppingCartIconConfiguration.isHidden = !visible
    }

    @objc
    func setCartItemCount(_ itemCounts: Int) {
        self.itemCounts = itemCounts

        guard let rProductInfoViewConfigurator = productInfoViewConfigurator else {
            return
        }

        rProductInfoViewConfigurator.1.shoppingCartIconConfiguration.indicator.isHidden = itemCounts == 0
    }

    @objc
    func updateVideoProducts(_ products: [AnyObject]?, cbId: NSNumber) {
        guard let productHydrating = productHydratingMap[Int(truncating: cbId)] else {
            return
        }

        productHydratingMap.removeValue(forKey: Int(truncating: cbId))

        guard let rProducts = RCTConvert.buildProducts(products) else {
            return
        }

        let variantsHydrationStrategy =
            productInfoViewConfiguration?.hydration?.variantsHydrationStrategy?.strategy ?? .merge

        for product in rProducts {
            productHydrating.hydrateProduct(
                product.productId, { build in
                    return ShoppingModule.hydrateProduct(
                        product, build,
                        variantsHydrationStrategy: variantsHydrationStrategy
                    )
                })
        }
    }

    @objc
    func setProductInfoViewConfiguration(_ config: [String: Any]?,
                                         resolver: RCTPromiseResolveBlock,
                                         rejecter: RCTPromiseRejectBlock) {
        let config = RCTConvert.buildProductInfoViewConfiguration(config)
        guard let rConfig = config else {
            resolver(false)
            return
        }

        productInfoViewConfiguration = rConfig
        resolver(true)

        guard let rProductInfoViewConfigurator = productInfoViewConfigurator else {
            return
        }

        ShoppingModule.hydrateProductViewConfig(rConfig, rProductInfoViewConfigurator.1)
    }

    @objc
    func clearCallbackId(_ cbId: NSNumber, eventName: String) {
        if eventName == ShoppingEventName.updateProductDetails.rawValue {
            productHydratingMap.removeValue(forKey: Int(truncating: cbId))
        } else if eventName == ShoppingEventName.ctaButtonClick.rawValue {
            if let handler = shoppingCTAHandlerMap[Int(truncating: cbId)] {
                handler(.none)
            }
            shoppingCTAHandlerMap.removeValue(forKey: Int(truncating: cbId))
        }
    }

    @objc
    func setCustomClickLinkButtonEnabled(
        _ enabled: Bool, resolver: @escaping RCTPromiseResolveBlock,
        rejecter: @escaping RCTPromiseRejectBlock
    ) {
        customClickLinkButtonEnabled = enabled
        resolver([:])
    }

    @objc
    func setCustomTapProductCardEnabled(
        _ enabled: Bool, resolver: @escaping RCTPromiseResolveBlock,
        rejecter: @escaping RCTPromiseRejectBlock
    ) {
        customTapProductCardEnabled = enabled
        resolver([:])
    }

    @objc
    func updateShoppingCTAResult(_ result: [String: Any]?, cbId: NSNumber) {
        guard let ctaResult = RCTConvert.buildShoppingCTAResult(result) else {
            return
        }

        guard let handler = shoppingCTAHandlerMap[Int(truncating: cbId)] else {
            return
        }

        if let res = ctaResult.res,
           let tips = ctaResult.tips,
           tips.count > 0 {
            switch res {
            case .success:
                handler(.feedbackOnly(.success(message: tips)))
            case .fail:
                handler(.feedbackOnly(.failure(message: tips)))
            }
        } else {
            handler(.none)
        }

        shoppingCTAHandlerMap.removeValue(forKey: Int(truncating: cbId))
    }

    @objc(pausePlayer:)
    func pausePlayer(_ callbackId: NSNumber) {
        if let playerViewController = playerMap.object(forKey: callbackId),
           let playerViewController = playerViewController as? PlayerViewController {
            playerViewController.pause()
        }
    }

    @objc(resumePlayer:)
    func resumePlayer(_ callbackId: NSNumber) {
        if let playerViewController = playerMap.object(forKey: callbackId),
           let playerViewController = playerViewController as? PlayerViewController {
            playerViewController.resume()
        }
    }

    // MARK: - FireworkVideoShoppingDelegate
    func fireworkShopping(
        _ fireworkShopping: FireworkVideoShopping,
        willDisplayProductInfo productInfoViewConfigurator: ProductInfoViewConfigurable,
        forVideo video: VideoDetails
    ) {
        let callbackId = ShoppingModule.generateCallbackId()
        self.productInfoViewConfigurator = (callbackId, productInfoViewConfigurator)
        if let cartIconVisible = self.cartIconVisible {
            productInfoViewConfigurator.shoppingCartIconConfiguration.isHidden = !cartIconVisible
        }
        productInfoViewConfigurator.shoppingCartIconConfiguration.indicator.isHidden = itemCounts == 0

        if let productInfoViewConfiguration = productInfoViewConfiguration {
            ShoppingModule.hydrateProductViewConfig(productInfoViewConfiguration, productInfoViewConfigurator)
        }
    }

    func fireworkShopping(
        _ fireworkShopping: FireworkVideoShopping, updateDetailsForProducts products: [ProductID],
        forVideo video: VideoDetails, _ productHydrator: ProductHydrating
    ) {
        let callbackId = ShoppingModule.generateCallbackId()
        self.productHydratingMap[callbackId] = productHydrator

        sendEvent(
            withName: ShoppingEventName.updateProductDetails.rawValue,
            body: [
                "productIds": products,
                "callbackId": callbackId,
                "video": video.jsObject
            ])
    }

    func fireworkShopping(
        _ fireworkShopping: FireworkVideoShopping,
        productVariantCTASelected item: SelectedProductVariant,
        fromVideo video: VideoDetails,
        ctaCompletionHandler: @escaping ShoppingCTAHandler
    ) {
        let callbackId = ShoppingModule.generateCallbackId()
        self.shoppingCTAHandlerMap[callbackId] = ctaCompletionHandler
        var body = item.jsObject
        body["callbackId"] = callbackId
        body["video"] = video.jsObject
        if let product = item.product,
           product.hasCustomPrimaryCTA,
           let customCTA = product.customCTA,
           let url = customCTA.url,
           let title = customCTA.title,
           let titleTranslation = customCTA.titleTranslation {
            body["customCTA"] = [
                "url": url,
                "titleKey": title,
                "title": titleTranslation
            ]
        }
        sendEvent(
            withName: ShoppingEventName.ctaButtonClick.rawValue,
            body: body)
    }

    func fireworkShopping(
        _ fireworkShopping: FireworkVideoShopping,
        didTapLinkButtonAt item: SelectedProductVariant,
        fromVideo video: VideoDetails,
        withURL itemURL: String
    ) -> Bool {
        if customClickLinkButtonEnabled {
            var body = item.jsObject
            body["video"] = video.jsObject
            sendEvent(
                withName: ShoppingEventName.customLinkButtonClick.rawValue,
                body: body)
        }
        return customClickLinkButtonEnabled
    }

    func fireworkShopping(
        _ fireworkShopping: FireworkVideoShopping,
        didTapCartIconForVideo videoDetails: VideoDetails
    ) {
        sendEvent(
            withName: ShoppingEventName.clickCartIcon.rawValue, body: [
                "video": videoDetails.jsObject
            ])
    }

    func fireworkShopping(
        _ fireworkShopping: FireworkVideoShopping,
        didTapProductVariant item: SelectedProductVariant,
        forVideo video: VideoDetails
    ) -> Bool {
        var body = item.jsObject
        body["video"] = video.jsObject

        if customTapProductCardEnabled {
            let callbackId = ShoppingModule.generateCallbackId()
            body["callbackId"] = callbackId as NSNumber
            if let presentedVC = RCTPresentedViewController(),
               let playerViewController = ShoppingModule.getTopPlayerViewController(presentedVC) {
                playerMap.setObject(playerViewController, forKey: callbackId as NSNumber)
            }
            sendEvent(
                withName: ShoppingEventName.customProductCardTap.rawValue,
                body: body)

        }

        sendEvent(
            withName: ShoppingEventName.productClick.rawValue,
            body: body)

        return customTapProductCardEnabled
    }
}

extension ShoppingModule {
    // swiftlint:disable cyclomatic_complexity
    fileprivate static func hydrateProductViewConfig(
        _ config: ProductInfoViewConfiguration,
        _ productInfoViewConfigurator: ProductInfoViewConfigurable
    ) {
        var productDetailsConfiguration = productInfoViewConfigurator.productDetailsConfiguration

        if let ctaButtonConfiguration = config.ctaButton {
            if let text = ctaButtonConfiguration.text {
                switch text {
                case .addToCart:
                    productDetailsConfiguration.ctaButton.text = .addToCart
                case .shopNow:
                    productDetailsConfiguration.ctaButton.text = .shopNow
                }
            }
            if let backgroundColor = ctaButtonConfiguration.backgroundColor,
                backgroundColor.count > 0,
                let color = backgroundColor.uicolor() {
                productDetailsConfiguration.ctaButton.buttonConfiguration.backgroundColor = color
            }

            if let textColor = ctaButtonConfiguration.textColor,
                textColor.count > 0,
                let color = textColor.uicolor() {
                productDetailsConfiguration.ctaButton.buttonConfiguration.textColor = color
            }

            if let fontSize = ctaButtonConfiguration.fontSize {
                let iOSFontInfo = ctaButtonConfiguration.iOSFontInfo ?? FontInfo()
                productDetailsConfiguration.ctaButton.buttonConfiguration.font = iOSFontInfo.getFont(fontSize)
            }
        }

        if let linkButtonConfiguration = config.linkButton {
            if let isHidden = linkButtonConfiguration.isHidden {
                productDetailsConfiguration.linkButtonConfiguration.isHidden = isHidden
            }
        }

        if let productCardConfiguration = config.productCard {
            hydrateProductCardConfig(productCardConfiguration, productInfoViewConfigurator)
        }

        if let productCardV2Configuration = config.productCardV2 {
            hydrateProductCardV2Config(productCardV2Configuration, productInfoViewConfigurator)
        }

        productInfoViewConfigurator.productDetailsConfiguration = productDetailsConfiguration
    }
    // swiftlint:enable cyclomatic_complexity
}

extension ShoppingModule {

    // swiftlint:disable:next cyclomatic_complexity
    fileprivate static func hydrateProduct(
        _ product: Product,
        _ pbuild: ProductBuilder,
        variantsHydrationStrategy: FireworkVideo.VariantsHydrationStrategy
    ) -> ProductBuilder {
            if let name = product.name {
                pbuild.name(name)
            }
            if let subtitle = product.subtitle {
                pbuild.subtitle(subtitle)
            }
            if let currency = product.currency {
                pbuild.currency(currency)
            }
            if let description = product.description {
                pbuild.description(description)
            }
            if let isAvailable = product.isAvailable {
                pbuild.isAvailable(isAvailable)
            }
            if let mainProductImage = product.mainProductImage {
                pbuild.mainProductImage(mainProductImage)
            }
            if let hidePrice = product.hidePrice {
                pbuild.hidePrice(hidePrice)
            }
            if let customCTATitleTranslation = product.customCTATitleTranslation {
                pbuild.customCTATitleTranslation(customCTATitleTranslation)
            }
            if let hidePrimaryCTA = product.hidePrimaryCTA {
                pbuild.hidePrimaryCTA(hidePrimaryCTA)
            }
            if let customCTATarget = product.customCTATarget {
                pbuild.customCTATarget(customCTATarget)
            }
            if let customCTAUrl = product.customCTAUrl {
                pbuild.customCTAUrl(customCTAUrl)
            }
            if let customCTATitle = product.customCTATitle {
                pbuild.customCTATitle(customCTATitle)
            }
            if let hidden = product.hidden {
                pbuild.hidden(hidden)
            }
            if let variants = product.units {
                pbuild.variants(variantsHydrationStrategy) { pvsBuild in
                    for variant in variants {
                        pvsBuild.variant(variant.unitId) { pvBuild in
                            return ShoppingModule.buildVariant(variant, pvBuild)
                        }
                    }
                    return pvsBuild
                }
            }
            return pbuild
    }

    fileprivate static func buildVariant(
        _ variant: Product.ProductUnit, _ pvBuild: ProductVariantBuilder
    ) -> ProductVariantBuilder {
        if let name = variant.name {
            pvBuild.name(name)
        }
        if let url = variant.url {
            pvBuild.url(url)
        }
        if let imageUrl = variant.imageUrl {
            pvBuild.imageUrl(imageUrl)
        }
        if let price = variant.price {
            if let amount = price.amount, let currencyCode = price.currencyCode {
                pvBuild.formattedPrice(Decimal(amount), currencyCode: currencyCode)
            }
        }
        if let originalPrice = variant.originalPrice {
            if let amount = originalPrice.amount, let currencyCode = originalPrice.currencyCode {
                pvBuild.formattedOriginalPrice(Decimal(amount), currencyCode: currencyCode)
            }
        }
        if let isAvailable = variant.isAvailable {
            pvBuild.isAvailable(isAvailable)
        }
        let options = ShoppingModule.buildVariantOption(variant.options)
        if let options = options {
            pvBuild.options(options)
        }
        return pvBuild
    }

    fileprivate static func buildVariantOption(_ options: [Product.VariantOption]?) -> [String: String]? {
        guard let options = options else {
            return nil
        }
        var result: [String: String] = [:]
        for option in options {
            guard let name = option.name, let value = option.value else {
                continue
            }
            result[name] = value
        }
        return result
    }
}

extension ShoppingModule {
    static func getTopPlayerViewController(_ viewController: UIViewController) -> UIViewController? {
        if viewController is PlayerViewController {
            return viewController
        }

        for childVC in viewController.children {
            if let result = getTopPlayerViewController(childVC) {
                return result
            }
        }

        return nil
    }
}

private extension ShoppingModule {
    // swiftlint:disable:next cyclomatic_complexity function_body_length
    static func hydrateProductCardConfig(
        _ config: ProductInfoViewConfiguration.ProductCardConfiguration,
        _ productInfoViewConfigurator: ProductInfoViewConfigurable
    ) {
        if let theme = config.theme {
            switch theme {
            case .light:
                productInfoViewConfigurator.productCardConfiguration.appearance = .light
            case .dark:
                productInfoViewConfigurator.productCardConfiguration.appearance = .dark
            }
        }

        if let text = config.ctaButtonText {
            switch text {
            case .shopNow:
                productInfoViewConfigurator.productCardConfiguration.ctaConfig.text = .shopNow
            case .buyNow:
                productInfoViewConfigurator.productCardConfiguration.ctaConfig.text = .buyNow
            }
        }

        if let cornerRadius = config.cornerRadius {
            productInfoViewConfigurator.productCardConfiguration.cornerRadius = cornerRadius
        }

        if let priceConfiguration = config.priceConfiguration {
//                if let isPriceHidden = priceConfiguration.isHidden {
//                    productInfoViewConfigurator.productCardConfiguration.priceConfiguration.isHidden = isPriceHidden
//                }
            if let axis = priceConfiguration.axis {
                switch axis {
                case .horizontal:
                    productInfoViewConfigurator.productCardConfiguration.priceConfiguration.axis = .horizontal
                case .vertical:
                    productInfoViewConfigurator.productCardConfiguration.priceConfiguration.axis = .vertical
                }
            }

            if let priceLabel = priceConfiguration.priceLabel {
                if let textColor = priceLabel.textColor,
                   textColor.count > 0,
                    let color = textColor.uicolor() {
                productInfoViewConfigurator.productCardConfiguration.priceConfiguration.priceLabel.textColor = color
                }
                if let fontSize = priceLabel.fontSize {
                    let iOSFontInfo = priceLabel.iOSFontInfo ?? FontInfo()
                    productInfoViewConfigurator
                        .productCardConfiguration
                        .priceConfiguration
                        .priceLabel
                        .font = iOSFontInfo.getFont(fontSize)
                }
                if let numberOfLines = priceLabel.numberOfLines {
                    productInfoViewConfigurator
                        .productCardConfiguration
                        .priceConfiguration
                        .priceLabel
                        .numberOfLines = numberOfLines
                }
            }

            if let originalPriceLabel = priceConfiguration.originalPriceLabel {
                if let textColor = originalPriceLabel.textColor,
                   textColor.count > 0,
                    let color = textColor.uicolor() {
                    productInfoViewConfigurator
                        .productCardConfiguration
                        .priceConfiguration
                        .originalPriceLabel
                        .textColor = color
                }
                if let fontSize = originalPriceLabel.fontSize {
                    let iOSFontInfo = originalPriceLabel.iOSFontInfo ?? FontInfo()
                    productInfoViewConfigurator
                        .productCardConfiguration
                        .priceConfiguration
                        .originalPriceLabel
                        .font = iOSFontInfo.getFont(fontSize)
                }
                if let numberOfLines = originalPriceLabel.numberOfLines {
                    productInfoViewConfigurator
                        .productCardConfiguration
                        .priceConfiguration
                        .originalPriceLabel
                        .numberOfLines = numberOfLines
                }
            }

            if let isPriceFirst = priceConfiguration.isPriceFirst {
                productInfoViewConfigurator.productCardConfiguration.priceConfiguration.isPriceFirst = isPriceFirst
            }
        }

        if let isCtaButtonHidden = config.isCtaButtonHidden {
            productInfoViewConfigurator.productCardConfiguration.ctaConfig.isHidden = isCtaButtonHidden
        }

        if let ctaButtonStyle = config.ctaButtonStyle {
            if let textColor = ctaButtonStyle.textColor,
               textColor.count > 0,
               let color = textColor.uicolor() {
                productInfoViewConfigurator.productCardConfiguration.ctaConfig.textColor = color
            }

            if let fontSize = ctaButtonStyle.fontSize {
                let iOSFontInfo = ctaButtonStyle.iOSFontInfo ?? FontInfo()
                productInfoViewConfigurator.productCardConfiguration.ctaConfig.font = iOSFontInfo.getFont(fontSize)
            }
        }

        if let width = config.width, let height = config.height {
            productInfoViewConfigurator.productCardConfiguration.size = CGSize(
                width: width,
                height: height
            )
        } else if let width = config.width {
            let originalSize = productInfoViewConfigurator.productCardConfiguration.size
            productInfoViewConfigurator.productCardConfiguration.size = CGSize(
                width: width,
                height: originalSize.height
            )
        } else if let height = config.height {
            let originalSize = productInfoViewConfigurator.productCardConfiguration.size
            productInfoViewConfigurator.productCardConfiguration.size = CGSize(
                width: originalSize.width,
                height: height
            )
        }

        if let backgroundColor = config.backgroundColor,
           backgroundColor.count > 0,
           let color = backgroundColor.uicolor() {
           productInfoViewConfigurator.productCardConfiguration.backgroundColor = color
        }

        if let iconConfiguration = config.iconConfiguration {
            if let cornerRadius = iconConfiguration.cornerRadius {
                productInfoViewConfigurator.productCardConfiguration.productIcon.cornerRadius = cornerRadius
            }
        }

        if let nameLabel = config.nameLabel {
            if let textColor = nameLabel.textColor,
               textColor.count > 0,
                let color = textColor.uicolor() {
                productInfoViewConfigurator.productCardConfiguration.nameLabel.textColor = color
            }
            if let fontSize = nameLabel.fontSize {
                let iOSFontInfo = nameLabel.iOSFontInfo ?? FontInfo()
                productInfoViewConfigurator.productCardConfiguration.nameLabel.font = iOSFontInfo.getFont(fontSize)
            }
            if let numberOfLines = nameLabel.numberOfLines {
                productInfoViewConfigurator.productCardConfiguration.nameLabel.numberOfLines = numberOfLines
            }
        }
    }
    static func hydrateProductCardV2Config(
        _ config: ProductInfoViewConfiguration.ProductCardV2Configuration,
        _ productInfoViewConfigurator: ProductInfoViewConfigurable
    ) {
        var productCardV2Configuration = productInfoViewConfigurator.productCardV2Configuration

        if let backgroundColor = color(from: config.backgroundColor) {
            productCardV2Configuration.backgroundColor = backgroundColor
        }

        if let cornerRadius = config.cornerRadius {
            productCardV2Configuration.cornerRadius = cornerRadius
        }

        if let borderColor = color(from: config.borderConfiguration?.color) {
            productCardV2Configuration.borderConfiguration.color = borderColor
        }

        if let textColor = color(from: config.nameLabel?.textColor) {
            productCardV2Configuration.nameLabel.textColor = textColor
        }

        if let textColor = color(from: config.priceConfiguration?.priceLabel?.textColor) {
            productCardV2Configuration.priceConfiguration.priceLabel.textColor = textColor
        }

        if let textColor = color(from: config.priceConfiguration?.discountLabel?.textColor) {
            productCardV2Configuration.priceConfiguration.discountLabel.textColor = textColor
        }

        if let textColor = color(from: config.priceConfiguration?.originalPriceLabel?.textColor) {
            productCardV2Configuration.priceConfiguration.originalPriceLabel.textColor = textColor
        }

        productInfoViewConfigurator.productCardV2Configuration = productCardV2Configuration
    }

    static func color(from value: String?) -> UIColor? {
        guard let value = value, value.isEmpty == false else {
            return nil
        }

        return value.uicolor()
    }
}
// swiftlint:enable file_length
