package solutions.tretter.mindmachine.billing

import android.app.Activity
import android.content.Context
import android.util.Log
import com.android.billingclient.api.AcknowledgePurchaseParams
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingClientStateListener
import com.android.billingclient.api.BillingFlowParams
import com.android.billingclient.api.BillingResult
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.QueryProductDetailsResult
import com.android.billingclient.api.PendingPurchasesParams
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchasesResponseListener
import com.android.billingclient.api.QueryProductDetailsParams
import com.android.billingclient.api.QueryPurchasesParams
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class BillingManager(
    context: Context,
    private val onNoAdsEntitlementChanged: (Boolean) -> Unit,
) {
    private val appContext = context.applicationContext

    private val _hasNoAds = MutableStateFlow(false)
    val hasNoAds: StateFlow<Boolean> = _hasNoAds.asStateFlow()

    private val _productDetails = MutableStateFlow<Map<String, ProductDetails>>(emptyMap())
    val productDetails: StateFlow<Map<String, ProductDetails>> = _productDetails.asStateFlow()

    private fun log(message: String) {
        Log.d("MindMachineBilling", message)
    }

    private fun logError(message: String) {
        Log.e("MindMachineBilling", message)
    }

    private val billingClient: BillingClient = BillingClient.newBuilder(appContext)
        .setListener { _, purchases ->
            if (purchases != null) {
                handlePurchases(purchases)
            }
        }
        .enablePendingPurchases(
	    PendingPurchasesParams.newBuilder()
                .enableOneTimeProducts()
                .build()
	)
        .build()

    @Volatile
    private var isConnecting = false
    
    fun start() {
        log("BillingManager.start() - isReady=${billingClient.isReady}, isConnecting=$isConnecting")
    
        if (billingClient.isReady) {
            log("Billing client already ready")
            refresh()
            return
        }
    
        if (isConnecting) {
            log("Billing client connection already in progress")
            return
        }
    
        isConnecting = true
        log("Calling startConnection()")
    
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(result: BillingResult) {
                isConnecting = false
                log("Billing setup finished: ${result.responseCode} ${result.debugMessage}")
    
                if (result.responseCode == BillingClient.BillingResponseCode.OK) {
		    refresh()
                } else {
                    logError("Billing setup failed: ${result.responseCode} ${result.debugMessage}")
                }
            }
    
            override fun onBillingServiceDisconnected() {
                isConnecting = false
                log("Billing service disconnected")
            }
        })
    }

    fun refresh() {
        log("refresh() called")
        queryProductDetails()
        queryActivePurchases()
    }

    fun launchPurchase(activity: Activity, productId: String) {
        log("launchPurchase() productId=$productId")
        val details = _productDetails.value[productId]
        if (details == null) {
            log("Product details not found for $productId")
            return
        }

        val productDetailsParams = when (details.productType) {
            BillingClient.ProductType.SUBS -> {
                val offerToken = details.subscriptionOfferDetails
                    ?.firstOrNull()
                    ?.offerToken
                if (offerToken == null) {
                    log("No subscription offer token for $productId")
                    return
                }
                BillingFlowParams.ProductDetailsParams.newBuilder()
                    .setProductDetails(details)
                    .setOfferToken(offerToken)
                    .build()
            }

            BillingClient.ProductType.INAPP -> {
                BillingFlowParams.ProductDetailsParams.newBuilder()
                    .setProductDetails(details)
                    .build()
            }

            else -> {
                log("Unknown product type for $productId")
                return
            }
        }

        val params = BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(listOf(productDetailsParams))
            .build()

        val result = billingClient.launchBillingFlow(activity, params)
        if (result.responseCode == BillingClient.BillingResponseCode.OK) {
            log("launchBillingFlow succeeded")
        } else {
            logError("launchBillingFlow failed: ${result.responseCode} ${result.debugMessage}")
        }
    }

    private fun queryProductDetails() {
        log("queryProductDetails()")
        if (!billingClient.isReady) {
            log("Billing client not ready")
            return
        }
    
        val featureResult = billingClient.isFeatureSupported(BillingClient.FeatureType.PRODUCT_DETAILS)
        log("PRODUCT_DETAILS support: ${featureResult.responseCode} ${featureResult.debugMessage}")
        if (featureResult.responseCode != BillingClient.BillingResponseCode.OK) {
            logError("PRODUCT_DETAILS feature not supported")
            return
        }
    
        queryProductDetailsForType(
            productType = BillingClient.ProductType.SUBS,
            productIds = listOf(BillingProducts.SUB_REMOVE_ADS_MONTHLY)
        )
    
        queryProductDetailsForType(
            productType = BillingClient.ProductType.INAPP,
            productIds = listOf(BillingProducts.INAPP_REMOVE_ADS_FOREVER)
        )
    }
    
    private fun queryProductDetailsForType(
        productType: String,
        productIds: List<String>
    ) {
        try {
            log("Querying $productType product details for $productIds")
    
            val products = productIds.map { productId ->
                QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(productId)
                    .setProductType(productType)
                    .build()
            }
    
            val params = QueryProductDetailsParams.newBuilder()
                .setProductList(products)
                .build()
    
            billingClient.queryProductDetailsAsync(params) { result, queryResult ->
                log("queryProductDetailsAsync($productType) result: ${result.responseCode} ${result.debugMessage}")
    
                val detailsList = queryResult.productDetailsList
                log("Fetched $productType products: ${detailsList.map { it.productId }}")
    
                if (queryResult.unfetchedProductList.isNotEmpty()) {
                    logError("Unfetched $productType products: ${queryResult.unfetchedProductList}")
                }
    
                if (result.responseCode == BillingClient.BillingResponseCode.OK) {
                    _productDetails.value = _productDetails.value + detailsList.associateBy { it.productId }
                    log("Product details cache now has: ${_productDetails.value.keys}")
                }
            }
        } catch (t: Throwable) {
            Log.e("MindMachineBilling", "Exception querying $productType product details", t)
        }
    }

    private fun queryActivePurchases() {
        log("queryActivePurchases()")
        if (!billingClient.isReady) {
            log("Billing client not ready")
            return
        }

        val listener = PurchasesResponseListener { result, purchases ->
            if (result.responseCode == BillingClient.BillingResponseCode.OK) {
                log("Query purchases returned ${purchases.size} items")
                handlePurchases(purchases)
            } else {
                logError("Query purchases failed: ${result.responseCode} ${result.debugMessage}")
            }
        }

        billingClient.queryPurchasesAsync(
            QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build(),
            listener
        )
        billingClient.queryPurchasesAsync(
            QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(),
            listener
        )
    }

    private fun handlePurchases(purchases: List<Purchase>) {
        log("handlePurchases count=${purchases.size}")
        val active = purchases.any { p ->
            p.purchaseState == Purchase.PurchaseState.PURCHASED &&
                p.products.any { it == BillingProducts.SUB_REMOVE_ADS_MONTHLY || it == BillingProducts.INAPP_REMOVE_ADS_FOREVER }
        }
        log("Active entitlement: $active")

        if (_hasNoAds.value != active) {
            log("Entitlement changed: ${_hasNoAds.value} -> $active")
            _hasNoAds.value = active
            onNoAdsEntitlementChanged(active)
        }

        // Acknowledge where needed
        purchases.filter { it.purchaseState == Purchase.PurchaseState.PURCHASED && !it.isAcknowledged }
            .forEach { purchase ->
                log("Acknowledging purchase: ${purchase.purchaseToken}")
                val params = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
                    .build()
                billingClient.acknowledgePurchase(params) { billingResult ->
                    if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                        log("Purchase acknowledged successfully")
                    } else {
                        logError("Failed to acknowledge purchase: ${billingResult.responseCode} ${billingResult.debugMessage}")
                    }
                }
            }
    }
}
