import ClerkClient
import ComposableArchitecture
import Dependencies
import Foundation
import HTTPTypes
import OpenAPIRuntime
import OpenAPIURLSession
import UIKit

extension HTTPField.Name {
    static let clientHeader = Self("X-Suno-Client")!
    /// Used to distinguish US vs. international users
    /// More reliable to publish this ourselves rather than rely on `Accept-Language`, even if the implementation
    /// is the same under-the-hood
    /// Also opens up alternative region fetching i.e. using StoreKit's `StoreFront.countryCode`
    static let regionHeader = Self("X-Suno-Region")!
    /// We pass this id to the backend in the header, which passes back a session ID for us to add to all analytics calls
    /// Not to be confused with Segment's `anonymousID`
    /// This ID should be consistent and unique to the device, generated once-per-install
    /// We use to `idfv` for this value, which may have different behavior than what we're using on other platforms.
    /// `idfv` is:
    /// - unique for apps that come from the same vendor (i.e. Suno) running on the same device
    /// - nullable, but main after the device has been restarted but before the user has unlocked the device
    /// - changes when the user deletes all of that vendor's apps from the device and reinstalls one of them
    /// - changes when installing test builds using Xcode or when installing an app on a device using ad-hoc distribution
    static let anonymousIdHeader = Self("anonymous-id")!
    /// This can be provided in the response headers if we provide a valid `anonymous-id`
    /// Its value is stored and primarily used for analytics
    static let sessionIdHeader = Self("session-id")!
}

struct CustomHeadersMiddleware: ClientMiddleware {
    @Shared(.inMemory(.analyticsSessionId)) var sessionId: String?

    package func intercept(
        _ request: HTTPRequest,
        body: HTTPBody?,
        baseURL: URL,
        operationID _: String,
        next: (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
    ) async throws -> (HTTPResponse, HTTPBody?) {
        var request = request
        if let info = Bundle.main.infoDictionary,
           let version = info["CFBundleShortVersionString"] as? String,
           let build = info["CFBundleVersion"] as? String
        {
            request.headerFields[.clientHeader] = "iOS \(version)-\(build)"
        }
        /// `Locale.Region` is nullable, but should only be `nil` when using custom `Locale`s
        /// We expect `Locale.current` to have a value here
        if let region = Locale.current.region {
            /// BCP 47 region subtag
            request.headerFields[.regionHeader] = region.identifier
        }
        if let anonymousID = APIClientV2.anonymousID {
            request.headerFields[.anonymousIdHeader] = anonymousID
        }

        /// Make the network call
        let responseTuple = try await next(request, body, baseURL)

        /// If we get a valid `sessionId` value back and it's different from the current value, we update it.
        /// If we don't get a `sessionId` back, which can happen if an endpoint hasn't implemented it yet, do not modify any existing `sessionId`s. This may change in the future.
        if let sessionId = responseTuple.0.headerFields[.sessionIdHeader], self.sessionId != sessionId {
            APIClientV2.$sessionID.withLock { $0 = sessionId }
        }

        /// Return the response. This middleware currently doesn't make changes to the response.
        return responseTuple
    }
}
