//
//  StoreManager.swift
//  SportsTime
//
//  Manages StoreKit 2 subscriptions and entitlements.
//

import Foundation
import StoreKit

@Observable
@MainActor
final class StoreManager {
    // MARK: - Singleton

    static let shared = StoreManager()

    // MARK: - Constants

    static let proProductIDs: Set<String> = [
        "com.88oakapps.SportsTime.pro.monthly",
        "com.88oakapps.SportsTime.pro.annual2"
    ]

    static let freeTripLimit = 1

    // MARK: - Published State

    private(set) var products: [Product] = []
    private(set) var purchasedProductIDs: Set<String> = []
    private(set) var isLoading = false
    private(set) var error: StoreError?

    /// Current subscription status details (nil if no subscription)
    private(set) var subscriptionStatus: SubscriptionStatusInfo?

    // MARK: - Debug Override (DEBUG builds only)

    #if DEBUG
    private static let debugProOverrideKey = "debugProOverride"

    /// When true, isPro returns true regardless of actual subscription status.
    /// Defaults to true in debug builds. Only compiled in DEBUG configuration.
    var debugProOverride: Bool {
        get {
            // Default to true by checking if key exists; object(forKey:) returns nil if not set
            UserDefaults.standard.object(forKey: Self.debugProOverrideKey) as? Bool ?? true
        }
        set {
            UserDefaults.standard.set(newValue, forKey: Self.debugProOverrideKey)
        }
    }
    #endif

    // MARK: - Computed Properties

    var isPro: Bool {
        #if DEBUG
        if debugProOverride { return true }
        #endif
        // Grant access if subscribed OR in grace period (billing retry)
        if let status = subscriptionStatus, status.isInGracePeriod {
            return true
        }
        return !purchasedProductIDs.intersection(Self.proProductIDs).isEmpty
    }

    var monthlyProduct: Product? {
        products.first { $0.id == "com.88oakapps.SportsTime.pro.monthly" }
    }

    var annualProduct: Product? {
        products.first { $0.id == "com.88oakapps.SportsTime.pro.annual2" }
    }

    // MARK: - Initialization

    private init() {}

    // MARK: - Product Loading

    func loadProducts() async {
        isLoading = true
        error = nil

        print("[StoreManager] Loading products for IDs: \(Self.proProductIDs)")

        do {
            let fetchedProducts = try await Product.products(for: Self.proProductIDs)
            products = fetchedProducts

            print("[StoreManager] Loaded \(fetchedProducts.count) products:")
            for product in fetchedProducts {
                print("[StoreManager]   - \(product.id): \(product.displayPrice) (\(product.displayName))")
                if let sub = product.subscription {
                    print("[StoreManager]     Subscription period: \(sub.subscriptionPeriod)")
                    if let intro = sub.introductoryOffer {
                        print("[StoreManager]     Intro offer: \(intro.paymentMode) for \(intro.period), price: \(intro.displayPrice)")
                    } else {
                        print("[StoreManager]     No intro offer")
                    }
                }
            }

            // Treat an empty fetch as a configuration issue so UI can show a useful fallback.
            if fetchedProducts.isEmpty {
                print("[StoreManager] WARNING: No products returned — check Configuration.storekit")
                error = .productNotFound
            }
        } catch {
            products = []
            self.error = .productNotFound
            print("[StoreManager] ERROR loading products: \(error)")
        }

        isLoading = false
    }

    // MARK: - Entitlement Management

    func updateEntitlements() async {
        var purchased: Set<String> = []

        for await result in Transaction.currentEntitlements {
            if case .verified(let transaction) = result {
                purchased.insert(transaction.productID)
            }
        }

        purchasedProductIDs = purchased

        // Update subscription status details
        await updateSubscriptionStatus()
        trackSubscriptionAnalytics(source: "entitlements_refresh")
    }

    // MARK: - Subscription Status

    private func updateSubscriptionStatus() async {
        // Find a Pro product to check subscription status
        guard let product = products.first(where: { Self.proProductIDs.contains($0.id) }),
              let subscription = product.subscription else {
            subscriptionStatus = nil
            return
        }

        do {
            let statuses = try await subscription.status
            guard let status = statuses.first(where: { $0.state != .revoked && $0.state != .expired }) ?? statuses.first else {
                subscriptionStatus = nil
                return
            }

            let renewalInfo = try? status.renewalInfo.payloadValue
            let transaction = try? status.transaction.payloadValue

            let planName: String
            if let productID = transaction?.productID {
                planName = productID.contains("annual") ? "Annual" : "Monthly"
            } else {
                planName = "Pro"
            }

            let state: SubscriptionState
            switch status.state {
            case .subscribed:
                state = .active
            case .inBillingRetryPeriod:
                state = .billingRetry
            case .inGracePeriod:
                state = .gracePeriod
            case .expired:
                state = .expired
            case .revoked:
                state = .revoked
            default:
                state = .active
            }

            subscriptionStatus = SubscriptionStatusInfo(
                state: state,
                planName: planName,
                productID: transaction?.productID,
                expirationDate: transaction?.expirationDate,
                gracePeriodExpirationDate: renewalInfo?.gracePeriodExpirationDate,
                willAutoRenew: renewalInfo?.willAutoRenew ?? false,
                isInGracePeriod: status.state == .inBillingRetryPeriod || status.state == .inGracePeriod
            )
        } catch {
            subscriptionStatus = nil
        }
    }

    // MARK: - Purchase

    func purchase(_ product: Product, source: String = "store_manager") async throws {
        AnalyticsManager.shared.trackPurchaseStarted(productId: product.id, source: source)
        let result = try await product.purchase()

        switch result {
        case .success(let verification):
            let transaction = try checkVerified(verification)
            await transaction.finish()
            await updateEntitlements()
            AnalyticsManager.shared.trackPurchaseCompleted(productId: product.id, source: source)
            trackSubscriptionAnalytics(source: "purchase_success")

        case .userCancelled:
            AnalyticsManager.shared.trackPurchaseFailed(productId: product.id, source: source, error: "user_cancelled")
            throw StoreError.userCancelled

        case .pending:
            // Ask to Buy or SCA - transaction will appear in updates when approved
            AnalyticsManager.shared.trackPurchaseFailed(productId: product.id, source: source, error: "pending")

        @unknown default:
            AnalyticsManager.shared.trackPurchaseFailed(productId: product.id, source: source, error: "unknown")
            throw StoreError.purchaseFailed
        }
    }

    // MARK: - Restore

    func restorePurchases(source: String = "settings") async {
        do {
            try await AppStore.sync()
        } catch {
            // Sync failed, but we can still check current entitlements
            AnalyticsManager.shared.trackPurchaseFailed(productId: nil, source: source, error: error.localizedDescription)
        }
        await updateEntitlements()
        AnalyticsManager.shared.trackPurchaseRestored(source: source)
        trackSubscriptionAnalytics(source: "restore")
    }

    // MARK: - Analytics

    func trackSubscriptionAnalytics(source: String) {
        let status: String
        let isSubscribed: Bool

        if let subscriptionStatus {
            switch subscriptionStatus.state {
            case .active:
                status = "subscribed"
                isSubscribed = true
            case .billingRetry:
                status = "billing_retry"
                isSubscribed = true
            case .gracePeriod:
                status = "grace_period"
                isSubscribed = true
            case .expired:
                status = "expired"
                isSubscribed = false
            case .revoked:
                status = "revoked"
                isSubscribed = false
            }
        } else {
            status = isPro ? "subscribed" : "free"
            isSubscribed = isPro
        }

        let type = subscriptionType(for: subscriptionStatus?.productID)
        AnalyticsManager.shared.trackSubscriptionStatusObserved(
            status: status,
            type: type,
            source: source,
            isSubscribed: isSubscribed,
            hasFullAccess: isPro,
            productId: subscriptionStatus?.productID,
            willAutoRenew: subscriptionStatus?.willAutoRenew,
            isInGracePeriod: subscriptionStatus?.isInGracePeriod,
            trialDaysRemaining: nil,
            expirationDate: subscriptionStatus?.expirationDate
        )
    }

    private func subscriptionType(for productID: String?) -> String {
        guard let productID else { return "none" }
        let id = productID.lowercased()
        if id.contains("annual") || id.contains("year") {
            return "yearly"
        }
        if id.contains("month") {
            return "monthly"
        }
        return "unknown"
    }

    // MARK: - Transaction Listener

    func listenForTransactions() -> Task<Void, Never> {
        Task.detached {
            for await result in Transaction.updates {
                if case .verified(let transaction) = result {
                    await transaction.finish()
                    await MainActor.run {
                        Task {
                            await StoreManager.shared.updateEntitlements()
                        }
                    }
                }
            }
        }
    }

    // MARK: - Helpers

    private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
        switch result {
        case .unverified:
            throw StoreError.verificationFailed
        case .verified(let safe):
            return safe
        }
    }
}

// MARK: - Subscription Status Info

enum SubscriptionState: String {
    case active
    case billingRetry
    case gracePeriod
    case expired
    case revoked

    var displayName: String {
        switch self {
        case .active: return "Active"
        case .billingRetry: return "Payment Issue"
        case .gracePeriod: return "Grace Period"
        case .expired: return "Expired"
        case .revoked: return "Revoked"
        }
    }

    var isActive: Bool {
        self == .active || self == .billingRetry || self == .gracePeriod
    }
}

struct SubscriptionStatusInfo {
    let state: SubscriptionState
    let planName: String
    let productID: String?
    let expirationDate: Date?
    let gracePeriodExpirationDate: Date?
    let willAutoRenew: Bool
    let isInGracePeriod: Bool

    var renewalDescription: String {
        guard let date = expirationDate else { return "" }
        let formatter = DateFormatter()
        formatter.dateStyle = .medium

        if state == .expired || state == .revoked {
            return "Expired \(formatter.string(from: date))"
        }

        if isInGracePeriod, let graceDate = gracePeriodExpirationDate {
            return "Payment due by \(formatter.string(from: graceDate))"
        }

        if willAutoRenew {
            return "Renews \(formatter.string(from: date))"
        } else {
            return "Expires \(formatter.string(from: date))"
        }
    }
}
