import Foundation

/// HTTP client for fetching view hierarchy on demand from the SDK's in-app server (port 8766).
/// Falls back gracefully when the SDK server isn't available (target app without SDK embedded).
public final class SdkHierarchyClient: SdkHierarchyFetching, @unchecked Sendable {
    private let baseURL: URL
    private let urlSession: URLSession

    public init(port: UInt16 = 8766) {
        self.baseURL = URL(string: "http://localhost:\(port)")!
        let config = URLSessionConfiguration.default
        config.timeoutIntervalForRequest = 2
        config.timeoutIntervalForResource = 5
        config.waitsForConnectivity = false
        self.urlSession = URLSession(configuration: config)
    }

    /// Fetch the latest cached hierarchy from the SDK (fast, no main-thread work in the target app).
    public func fetchHierarchy() -> SdkViewHierarchy? {
        return fetchSync(path: "/hierarchy")
    }

    /// Request a fresh hierarchy walk from the SDK (slower, involves main-thread work in the target app).
    public func fetchFreshHierarchy() -> SdkViewHierarchy? {
        return fetchSync(path: "/hierarchy/fresh")
    }

    /// Whether the SDK hierarchy server is reachable.
    public func isAvailable() -> Bool {
        return requestSync(path: "/health", session: healthSession) != nil
    }

    // MARK: - Private

    private lazy var healthSession: URLSession = {
        let config = URLSessionConfiguration.default
        config.timeoutIntervalForRequest = 0.5
        config.waitsForConnectivity = false
        return URLSession(configuration: config)
    }()

    private func fetchSync(path: String) -> SdkViewHierarchy? {
        guard let data = requestSync(path: path, session: urlSession) else { return nil }
        return try? JSONDecoder().decode(SdkViewHierarchy.self, from: data)
    }

    private func requestSync(path: String, session: URLSession) -> Data? {
        let url = baseURL.appendingPathComponent(path)
        let semaphore = DispatchSemaphore(value: 0)
        var result: Data?

        session.dataTask(with: url) { data, response, _ in
            defer { semaphore.signal() }
            guard let http = response as? HTTPURLResponse,
                  http.statusCode == 200,
                  let data = data else { return }
            result = data
        }.resume()

        semaphore.wait()
        return result
    }
}
