import Foundation

public enum HTTPMethod: String, Sendable {
    case get = "GET"
    case post = "POST"
    case head = "HEAD"
}

public enum HTTPClientError: Error, LocalizedError {
    case invalidResponse
    case badStatus(code: Int, responseBody: String)

    public var errorDescription: String? {
        switch self {
        case .invalidResponse:
            return "Invalid HTTP response"
        case .badStatus(let code, let responseBody):
            return "HTTP \(code): \(responseBody)"
        }
    }
}

public final class HTTPClient: @unchecked Sendable {
    private let session: URLSession

    public init(session: URLSession = .shared) {
        self.session = session
    }

    public func send(
        url: URL,
        method: HTTPMethod,
        headers: [String: String] = [:],
        body: Data? = nil,
        timeout: TimeInterval = 10
    ) async throws -> Data {
        var request = URLRequest(url: url)
        request.httpMethod = method.rawValue
        request.timeoutInterval = timeout
        request.httpBody = body
        for (key, value) in headers {
            request.setValue(value, forHTTPHeaderField: key)
        }

        let (data, response) = try await session.data(for: request)
        guard let httpResponse = response as? HTTPURLResponse else {
            throw HTTPClientError.invalidResponse
        }
        guard (200..<400).contains(httpResponse.statusCode) else {
            throw HTTPClientError.badStatus(
                code: httpResponse.statusCode,
                responseBody: String(data: data, encoding: .utf8) ?? "<binary>"
            )
        }
        return data
    }
}
