import Foundation
import SwiftUI
import GravitySDK

// Адаптер API приложения, не метод Gravity SDK.
// Запрос должен использовать авторизацию именно accountKey и поддерживать отмену.
protocol ReviewEligibilityAPI {
    func eligibleSkus(accountKey: String, skus: [String]) async throws -> Set<String>
}

final class ReviewPromptIntegration {
    private let api: ReviewEligibilityAPI
    private let selector: String
    private let currentAccountKey: () -> String? // Потокобезопасный снимок аккаунта.
    private let fallback: ProductViewBuilder
    private let onOpenReview: (String, String) -> Void
    private let submitted = SubmittedReviews()

    init(
        api: ReviewEligibilityAPI,
        selector: String,
        currentAccountKey: @escaping () -> String?,
        fallbackProductViewBuilder: ProductViewBuilder,
        onOpenReview: @escaping (String, String) -> Void
    ) {
        self.api = api
        self.selector = selector
        self.currentAccountKey = currentAccountKey
        self.fallback = fallbackProductViewBuilder
        self.onOpenReview = onOpenReview
    }

    func preparation() -> InAppPreparation {
        InAppPreparation(timeout: 5) { [self] content, campaign in
            guard campaign.selector == selector else { return content }
            guard let accountKey = currentAccountKey() else { return nil }
            let skus = content.products?.slots?.compactMap {
                $0.item["sku"] as? String
            } ?? []
            guard !skus.isEmpty else { return nil }

            // Один batch-запрос для всех кандидатов; исключения обрабатывает SDK.
            let eligible = try await api.eligibleSkus(
                accountKey: accountKey, skus: Array(Set(skus))
            )
            guard currentAccountKey() == accountKey else { return nil }
            return content.filteringProducts { slot in
                guard let sku = slot.item["sku"] as? String else { return false }
                return eligible.contains(sku) && !submitted.contains(accountKey, sku)
            }
        }
    }

    var productViewBuilder: ProductViewBuilder {
        ReviewProductViewBuilder(
            selector: selector, currentAccountKey: currentAccountKey,
            fallback: fallback, onOpenReview: onOpenReview
        )
    }

    // Вызывайте после успешного сохранения отзыва в API приложения.
    // accountKey берите из открытой формы, а не из текущего аккаунта в момент ответа.
    func didSubmitReview(accountKey: String, sku: String) {
        submitted.insert(accountKey, sku)
    }
}

private final class SubmittedReviews {
    private let lock = NSLock()
    private var byAccount: [String: Set<String>] = [:]

    func contains(_ accountKey: String, _ sku: String) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return byAccount[accountKey]?.contains(sku) ?? false
    }

    func insert(_ accountKey: String, _ sku: String) {
        lock.lock()
        defer { lock.unlock() }
        byAccount[accountKey, default: []].insert(sku)
    }
}

private struct ReviewProductViewBuilder: ProductViewBuilder {
    let selector: String
    let currentAccountKey: () -> String?
    let fallback: ProductViewBuilder
    let onOpenReview: (String, String) -> Void

    func build(slot: Slot, content: CampaignContent, campaign: Campaign) -> AnyView {
        guard campaign.selector == selector else {
            return fallback.build(slot: slot, content: content, campaign: campaign)
        }
        guard let sku = slot.item["sku"] as? String,
              let accountKey = currentAccountKey() else { return AnyView(EmptyView()) }

        return AnyView(
            VStack(alignment: .leading, spacing: 8) {
                Text(slot.item["name"] as? String ?? sku)
                Button("Оставить отзыв") {
                    guard currentAccountKey() == accountKey else { return }
                    GravitySDK.instance.sendProductEngagement(
                        engagement: ProductClickEngagement(
                            slot: slot, content: content, campaign: campaign
                        )
                    )
                    GravitySDK.instance.dismissInApp(contentId: content.contentId) { dismissed in
                        if dismissed && currentAccountKey() == accountKey {
                            onOpenReview(accountKey, sku)
                        }
                    }
                }
            }
            .frame(width: 160, alignment: .leading)
        )
    }
}
