//
//  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.sportstime.pro.monthly",
        "com.sportstime.pro.annual"
    ]

    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?

    // 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
        return !purchasedProductIDs.intersection(Self.proProductIDs).isEmpty
    }

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

    var annualProduct: Product? {
        products.first { $0.id == "com.sportstime.pro.annual" }
    }

    // MARK: - Initialization

    private init() {}

    // MARK: - Product Loading

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

        do {
            products = try await Product.products(for: Self.proProductIDs)
            isLoading = false
        } catch {
            self.error = .productNotFound
            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
    }

    // MARK: - Purchase

    func purchase(_ product: Product) async throws {
        let result = try await product.purchase()

        switch result {
        case .success(let verification):
            let transaction = try checkVerified(verification)
            await transaction.finish()
            await updateEntitlements()

        case .userCancelled:
            throw StoreError.userCancelled

        case .pending:
            // Ask to Buy or SCA - transaction will appear in updates when approved
            break

        @unknown default:
            throw StoreError.purchaseFailed
        }
    }

    // MARK: - Restore

    func restorePurchases() async {
        do {
            try await AppStore.sync()
        } catch {
            // Sync failed, but we can still check current entitlements
        }
        await updateEntitlements()
    }

    // 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
        }
    }
}
