//
//  Store.swift
//  Poker Analytics 6
//
//  Created by Laurent Morvillier on 20/04/2022.
//

import Foundation
import StoreKit

protocol StoreDelegate {
    func productsReceived()
    func errorDidOccur(error: Error)
}

class Store: ObservableObject {
    
    @Published private(set) var products: [Product] = []
    
    @Published private(set) var purchasedTransactions = Set<StoreKit.Transaction>()

    var delegate: StoreDelegate? = nil
    
    var updateListenerTask: Task<Void, Error>? = nil

    init() {
        
        self.updateListenerTask = listenForTransactions()

        Task {
            //Initialize the store by starting a product request.
            await self.requestProducts()
        }
    }
    
    deinit {
        self.updateListenerTask?.cancel()
    }

    func indexOf(identifier: String) -> Int? {
        return self.products.map { $0.id }.firstIndex(of: identifier)
    }
    
    @MainActor
    func requestProducts() async {
        do {
            let identifiers: [String] = [StorePlan.unlimited.rawValue]
            products = try await Product.products(for: identifiers)
            Logger.log("products = \(self.products.count)")
            self.delegate?.productsReceived()
        } catch {
            self.delegate?.errorDidOccur(error: error)
            Logger.error(error)
        }
    }
    
    func listenForTransactions() -> Task<Void, Error> {
        return Task.detached {
            //Iterate through any transactions which didn't come from a direct call to `purchase()`.
            for await result in Transaction.updates {
                do {
                    
                    let transaction = try await AppGuard.main.processTransactionResult(result)

                    //Always finish a transaction.
                    await transaction.finish()
                } catch {
                    self.delegate?.errorDidOccur(error: error)
                    
                    //StoreKit has a receipt it can read but it failed verification. Don't deliver content to the user.
                    print("Transaction failed verification")
                }
            }
        }
    }

    func purchase(_ product: Product) async throws -> StoreKit.Transaction? {
        // Begin a purchase.
        let result = try await product.purchase()

        switch result {
        case .success(let verificationResult):
            
            let transaction = try await AppGuard.main.processTransactionResult(verificationResult)

            // Always finish a transaction.
            await transaction.finish()

            DispatchQueue.main.asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 200000), execute: {
                NotificationCenter.default.post(name: Notification.Name.StoreEventHappened, object: nil)
            })
                                          
            return transaction
        case .userCancelled, .pending:
            return nil
        default:
            return nil
        }
    }

}
