//
//  CatFacts.swift
//  CatFactsKit
//

import Foundation

public struct CatFacts: Sendable {
    public let baseURL: URL
    let urlSession: URLSession

    public init(baseURL: URL, urlSession: URLSession = .shared) {
        self.baseURL = baseURL
        self.urlSession = urlSession
    }

    public func getFacts(count: Int? = nil) async throws -> [String] {
        let params = count.map { ["count": $0] } ?? [:]
        let request = try generateRequest(path: "/", params: params)
        let response: FactsResponse = try await decodeRequest(request: request)
        return response.data
    }

    // MARK: - Private functionality

    private func generateRequest(path: String, params: [String: CustomStringConvertible] = [:]) throws -> URLRequest {
        guard let url = URL(string: path, relativeTo: baseURL) else {
            throw CatFactsError.requestError("Couldn't generate URL from path: \(path), baseURL: \(baseURL)")
        }

        let queryItems = params
            .compactMap { URLQueryItem(name: $0, value: $1.description) }
            .sorted { a, b in a.name < b.name }
        return queryItems.count > 0
            ? URLRequest(url: url.appending(queryItems: queryItems))
            : URLRequest(url: url)
    }

    private func decodeRequest<T: Decodable>(request: URLRequest) async throws -> T {
        let (data, response) = try await urlSession.data(for: request)
        guard let response = response as? HTTPURLResponse else {
            throw CatFactsError.connectionError("Couldn't get HTTP response from request \(request)")
        }
        guard response.statusCode < 300 else {
            let body = String(data: data, encoding: .utf8)
            throw CatFactsError.responseError(response.statusCode, body ?? "unknown error")
        }
        return try JSONDecoder().decode(T.self, from: data)
    }
}
