package com.approagency.pharmacy.data.billing

import android.app.Activity
import android.content.Context
import com.approagency.pharmacy.BuildConfig
import com.approagency.pharmacy.data.local.SessionManager
import com.approagency.pharmacy.domain.billing.PurchaseGateway
import com.approagency.pharmacy.domain.billing.PurchaseResult
import com.approagency.pharmacy.domain.model.SubscriptionProduct
import ir.myket.billingclient.IabHelper
import ir.myket.billingclient.util.Purchase
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.UUID
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

/**
 * درگاهِ پرداختِ مایکت بر پایهٔ کتابخانهٔ `myket-billing-client` (IabHelper).
 *
 * جریان (مطابق همان مدلی که در اپ فلاترِ آپرواجنسی استفاده شده):
 *  1. اتصال به سرویس مایکت ([ensureSetup]).
 *  2. شروع خرید با SKU = [SubscriptionProduct.uuid] و دریافت توکنِ خرید.
 *  3. توکن به سرور فرستاده می‌شود (در لایهٔ اشتراک) و پس از تأیید، خرید [consume]
 *     می‌شود تا کاربر بتواند دورهٔ بعد دوباره خرید کند.
 *
 * چون IabHelper در هر لحظه فقط یک عملیاتِ ناهمگام را می‌پذیرد، همهٔ فراخوانی‌ها
 * با [mutex] ترتیبی می‌شوند.
 */
class MyketPurchaseGateway(
    context: Context,
    private val session: SessionManager
) : PurchaseGateway {

    override val name: String = "myket"
    override val isAvailable: Boolean = true

    private val appContext = context.applicationContext
    private val mutex = Mutex()

    @Volatile private var helper: IabHelper? = null
    @Volatile private var setupDone = false

    /** خریدهای منتظرِ مصرف، برحسبِ توکن. */
    private val pending = mutableMapOf<String, Purchase>()

    override suspend fun purchase(
        activity: Activity,
        product: SubscriptionProduct
    ): Result<PurchaseResult> = mutex.withLock {
        val sku = product.uuid?.takeIf { it.isNotBlank() }
            ?: return Result.failure(IllegalArgumentException("شناسهٔ محصول (SKU) نامعتبر است."))

        ensureSetup().onFailure { return Result.failure(it) }
        val helper = helper ?: return Result.failure(IllegalStateException("اتصال به مایکت برقرار نشد."))

        // developerPayload = شماره موبایلِ کاربر برای شناساییِ خریدار؛ در صورت نبود، توکن تصادفی.
        val payload = session.account.value.mobile?.takeIf { it.isNotBlank() }
            ?: UUID.randomUUID().toString()

        val outcome = runCatching {
            suspendCancellableCoroutine { cont ->
                helper.launchPurchaseFlow(
                    activity,
                    sku,
                    IabHelper.OnIabPurchaseFinishedListener { result, purchase ->
                        when {
                            result.isSuccess && purchase != null ->
                                cont.resume(PurchaseOutcome.Success(purchase))
                            result.response == IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED ->
                                cont.resume(PurchaseOutcome.AlreadyOwned)
                            else ->
                                cont.resume(PurchaseOutcome.Error(result.message ?: "خطای پرداخت مایکت"))
                        }
                    },
                    payload
                )
            }
        }.getOrElse { return Result.failure(it) }

        val purchase = when (outcome) {
            is PurchaseOutcome.Success -> outcome.purchase
            PurchaseOutcome.AlreadyOwned -> recoverOwned(sku)
                ?: return Result.failure(IllegalStateException("این محصول قبلاً خریداری شده است."))
            is PurchaseOutcome.Error -> return Result.failure(IllegalStateException(outcome.message))
        }

        val token = purchase.token
        if (token.isNullOrBlank()) {
            return Result.failure(IllegalStateException("توکن خرید دریافت نشد."))
        }
        pending[token] = purchase
        Result.success(PurchaseResult(purchaseToken = token, orderId = purchase.orderId))
    }

    override suspend fun consume(purchaseToken: String): Result<Unit> = mutex.withLock {
        val purchase = pending[purchaseToken] ?: return Result.success(Unit)
        val helper = helper ?: return Result.success(Unit)
        runCatching {
            suspendCancellableCoroutine { cont ->
                helper.consumeAsync(purchase, IabHelper.OnConsumeFinishedListener { _, result ->
                    if (result.isSuccess) cont.resume(Unit)
                    else cont.resumeWithException(IllegalStateException(result.message ?: "خطای مصرفِ خرید"))
                })
            }
        }.onSuccess { pending.remove(purchaseToken) }
    }

    /** اتصال به سرویس مایکت (یک‌بار). */
    private suspend fun ensureSetup(): Result<Unit> {
        if (setupDone && helper != null) return Result.success(Unit)
        val newHelper = IabHelper(appContext, BuildConfig.IAB_PUBLIC_KEY)
            .apply { enableDebugLogging(BuildConfig.DEBUG) }
        return runCatching {
            suspendCancellableCoroutine { cont ->
                newHelper.startSetup(IabHelper.OnIabSetupFinishedListener { result ->
                    if (result.isSuccess) cont.resume(Unit)
                    else cont.resumeWithException(
                        IllegalStateException(result.message ?: "اتصال به مایکت ناموفق بود")
                    )
                })
            }
        }.onSuccess {
            helper = newHelper
            setupDone = true
        }.onFailure {
            runCatching { newHelper.dispose() }
        }
    }

    /** بازیابیِ خریدِ مصرف‌نشدهٔ موجود (وقتی محصول قبلاً خریده شده). */
    private suspend fun recoverOwned(sku: String): Purchase? {
        val helper = helper ?: return null
        return runCatching {
            suspendCancellableCoroutine<Purchase?> { cont ->
                helper.queryInventoryAsync(
                    false,
                    listOf(sku),
                    IabHelper.QueryInventoryFinishedListener { result, inventory ->
                        cont.resume(if (result.isSuccess) inventory.getPurchase(sku) else null)
                    }
                )
            }
        }.getOrNull()
    }

    private sealed interface PurchaseOutcome {
        data class Success(val purchase: Purchase) : PurchaseOutcome
        data object AlreadyOwned : PurchaseOutcome
        data class Error(val message: String) : PurchaseOutcome
    }
}
