import Foundation

/// Concurrency-safe access-token holder with SINGLE-FLIGHT refresh: concurrent 401s must not each
/// fire `/auth/refresh`. The coalescing is via a stored `Task` handle (not a bool) — entrants await
/// the same in-flight task; it's cleared (success OR failure) when complete. Clearing too early lets a
/// follower re-refresh; never clearing means the next expiry won't refresh.
public actor TokenProvider {
    private var cached: String?
    private let persistence: TokenPersistence
    private let refresh: @Sendable () async throws -> String
    private var inFlightRefresh: Task<String, Error>?

    public init(persistence: TokenPersistence, refresh: @escaping @Sendable () async throws -> String) {
        self.persistence = persistence
        self.refresh = refresh
        self.cached = persistence.read()
    }

    public func current() -> String? { cached ?? persistence.read() }

    public func set(_ token: String) {
        cached = token
        try? persistence.save(token)
    }

    public func clear() {
        cached = nil
        try? persistence.delete()
    }

    public func refreshOnce() async throws -> String {
        if let task = inFlightRefresh {
            return try await task.value
        }
        let task = Task { try await self.refresh() }
        inFlightRefresh = task
        defer { inFlightRefresh = nil }
        let token = try await task.value
        cached = token
        try? persistence.save(token)
        return token
    }
}
