//
//  NavvoMaps.swift
//  Navvo Maps SDK for iOS
//
//  Global configuration shared by the map view (`NavvoMapView` / `NavvoMap`)
//  and the services client (`NavvoServices`).
//

import Foundation

/// Global Navvo configuration.
///
/// Call ``configure(apiKey:baseURL:defaultStyle:)`` once at app start (for
/// example in `application(_:didFinishLaunchingWithOptions:)`) before creating
/// a map view or using `NavvoServices.shared`:
///
/// ```swift
/// NavvoMaps.configure(apiKey: "nvvo_YOUR_KEY")
/// ```
///
/// REST calls send the key as the `x-api-key` header; map style/tile URLs
/// carry it as `?api_key=` (those requests are made by the renderer and cannot
/// set headers).
public enum NavvoMaps {

    /// The default Navvo API base URL.
    public static let defaultBaseURL = URL(string: "https://navvo.io/api/v1")!

    private static let lock = NSLock()
    private static var _apiKey: String?
    private static var _baseURL: URL = defaultBaseURL
    private static var _defaultStyle: NavvoMapStyle = .standard

    /// Configure the global API key (and optionally the base URL and the
    /// default style for new maps).
    ///
    /// - Parameters:
    ///   - apiKey: A Navvo API key (`nvvo_<48 hex>`).
    ///   - baseURL: Override the API base URL (staging / self-hosted). The
    ///     default `https://navvo.io/api/v1` is kept when `nil`.
    ///   - defaultStyle: The style new `NavvoMapView`s use when none is given.
    public static func configure(
        apiKey: String,
        baseURL: URL? = nil,
        defaultStyle: NavvoMapStyle = .standard
    ) {
        lock.lock()
        defer { lock.unlock() }
        _apiKey = apiKey
        if let baseURL { _baseURL = normalized(baseURL) }
        _defaultStyle = defaultStyle
    }

    /// Convenience alias for ``configure(apiKey:baseURL:defaultStyle:)``.
    public static func provideAPIKey(_ apiKey: String) {
        configure(apiKey: apiKey)
    }

    /// Whether a key has been configured.
    public static var isConfigured: Bool {
        lock.lock()
        defer { lock.unlock() }
        return !(_apiKey ?? "").isEmpty
    }

    /// The configured API base URL (no trailing slash).
    public static var baseURL: URL {
        lock.lock()
        defer { lock.unlock() }
        return _baseURL
    }

    /// The default map style for new maps.
    public static var defaultStyle: NavvoMapStyle {
        lock.lock()
        defer { lock.unlock() }
        return _defaultStyle
    }

    /// The configured key redacted to `nvvo_****<last4>` — safe to log.
    /// The full key is never logged by the SDK.
    public static var redactedAPIKey: String {
        lock.lock()
        defer { lock.unlock() }
        guard let key = _apiKey, key.count >= 4 else { return "nvvo_****" }
        return "nvvo_****\(key.suffix(4))"
    }

    /// The configured key, or a thrown ``NavvoError/config(_:)`` when unset.
    static func requireAPIKey() throws -> String {
        lock.lock()
        defer { lock.unlock() }
        guard let key = _apiKey, !key.isEmpty else {
            throw NavvoError.config(
                "Navvo is not configured. Call NavvoMaps.configure(apiKey:) "
                    + "before using the map or services."
            )
        }
        return key
    }

    /// Build a renderer-fetchable style URL with the key as `?api_key=`.
    ///
    /// Style/tile URLs are fetched by the rendering engine and cannot carry
    /// headers, so this is the only place the key travels in a URL.
    ///
    /// ```swift
    /// let url = try NavvoMaps.styleURL(for: .dark)
    /// // https://navvo.io/api/v1/tiles/style?style=dark&api_key=nvvo_...
    /// ```
    ///
    /// - Parameters:
    ///   - style: One of the six Navvo basemap styles.
    ///   - terrain: Append `terrain=1` (pairs with ``NavvoMapStyle/threeD``).
    public static func styleURL(for style: NavvoMapStyle, terrain: Bool = false) throws -> URL {
        let key = try requireAPIKey()
        let base = baseURL
        guard var components = URLComponents(
            string: base.absoluteString + "/tiles/style"
        ) else {
            throw NavvoError.config("invalid base URL: \(base.absoluteString)")
        }
        var items = [
            URLQueryItem(name: "style", value: style.rawValue),
            URLQueryItem(name: "api_key", value: key),
        ]
        if terrain { items.append(URLQueryItem(name: "terrain", value: "1")) }
        components.queryItems = items
        guard let url = components.url else {
            throw NavvoError.config("could not build style URL")
        }
        return url
    }

    /// Reset all global configuration (primarily for tests).
    public static func reset() {
        lock.lock()
        defer { lock.unlock() }
        _apiKey = nil
        _baseURL = defaultBaseURL
        _defaultStyle = .standard
    }

    private static func normalized(_ url: URL) -> URL {
        var absolute = url.absoluteString
        while absolute.hasSuffix("/") { absolute.removeLast() }
        return URL(string: absolute) ?? url
    }
}
