import StoreKit
import UIKit

final class ReviewPromptService {
    static let shared = ReviewPromptService()

    private let lastPromptKey = "reviewPromptLastDate"
    private let checkInCountKey = "reviewPromptCheckinCount"
    private let hasCompletedStoreReviewKey = "reviewPromptHasCompletedStoreReview"
    private let promptedAchievementKeysKey = "reviewPromptedAchievementKeys"
    // Rating velocity: with a monthly-cadence app, 3 check-ins + 90 days meant 3+ months
    // before the first review could even be requested. 2 check-ins + 30 days keeps the
    // prompt tied to a good moment (a completed check-in) while building ratings sooner.
    private let minCheckInsBetweenPrompts = 2
    private let minDaysBetweenPrompts = 30
    private let userDefaults: UserDefaults
    private let dateProvider: () -> Date
    private let reviewRequestHandler: () -> Void

    private init() {
        userDefaults = .standard
        dateProvider = Date.init
        reviewRequestHandler = ReviewPromptService.defaultReviewRequestHandler
    }

    init(
        userDefaults: UserDefaults,
        dateProvider: @escaping () -> Date,
        reviewRequestHandler: @escaping () -> Void
    ) {
        self.userDefaults = userDefaults
        self.dateProvider = dateProvider
        self.reviewRequestHandler = reviewRequestHandler
    }

    func recordMonthlyCheckInCompleted() {
        let currentCount = userDefaults.integer(forKey: checkInCountKey)
        userDefaults.set(currentCount + 1, forKey: checkInCountKey)
        requestReviewIfEligible()
    }

    func shouldAskForAchievementSatisfaction(newlyUnlockedAchievementKeys: Set<String>) -> Bool {
        guard !newlyUnlockedAchievementKeys.isEmpty else { return false }
        guard !hasCompletedStoreReview else { return false }

        let promptedKeys = Set(userDefaults.stringArray(forKey: promptedAchievementKeysKey) ?? [])
        let unpromptedKeys = newlyUnlockedAchievementKeys.subtracting(promptedKeys)
        guard !unpromptedKeys.isEmpty else { return false }

        let mergedKeys = promptedKeys.union(unpromptedKeys).sorted()
        userDefaults.set(mergedKeys, forKey: promptedAchievementKeysKey)
        return true
    }

    var hasCompletedStoreReview: Bool {
        userDefaults.bool(forKey: hasCompletedStoreReviewKey)
    }

    func markStoreReviewCompleted() {
        userDefaults.set(true, forKey: hasCompletedStoreReviewKey)
    }

    static func appStoreWriteReviewURL() -> URL {
        guard var components = URLComponents(url: GoalShareService.appStoreURL, resolvingAgainstBaseURL: false) else {
            return GoalShareService.appStoreURL
        }
        var queryItems = components.queryItems ?? []
        queryItems.removeAll { $0.name == "action" }
        queryItems.append(URLQueryItem(name: "action", value: "write-review"))
        components.queryItems = queryItems
        return components.url ?? GoalShareService.appStoreURL
    }

    private func requestReviewIfEligible() {
        guard !hasCompletedStoreReview else { return }
        let now = dateProvider()
        if let lastPrompt = userDefaults.object(forKey: lastPromptKey) as? Date {
            let daysSince = now.timeIntervalSince(lastPrompt) / 86_400
            if daysSince < Double(minDaysBetweenPrompts) {
                return
            }
        }

        let count = userDefaults.integer(forKey: checkInCountKey)
        guard count >= minCheckInsBetweenPrompts else { return }

        requestReview()
    }

    private func requestReview() {
        reviewRequestHandler()
        userDefaults.set(dateProvider(), forKey: lastPromptKey)
        userDefaults.set(0, forKey: checkInCountKey)
    }

    private static func defaultReviewRequestHandler() {
        guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return }
        if #available(iOS 18.0, *) {
            AppStore.requestReview(in: scene)
        } else {
            SKStoreReviewController.requestReview(in: scene)
        }
    }
}
