import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

public struct ApiClient: Sendable {
    static let apiUrl: URL = URL(string: "https://discord.com/api/v10")!
    let token: String

    @discardableResult
    private func authReq(_ request: consuming URLRequest) async throws -> (Data, URLResponse) {
        request.setValue("Bot \(token)", forHTTPHeaderField: "Authorization")
        request.setValue("DiscordKit (https:\\candy123.moe, v0.0.1)", forHTTPHeaderField: "User-Agent")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        let (data, res) = try await URLSession.shared.data(for: request)
        guard let res = res as? HTTPURLResponse else { throw ApiError.invalidResponse }
        guard res.statusCode >= 200 && res.statusCode <= 299 else { throw ApiError.badStatus("Status code not 2xx: \(res.statusCode)") }

        return (data, res)
    }

    func getGatewayURL() async throws -> URL {
        var req = URLRequest(url: ApiClient.apiUrl.appending(path: "/gateway/bot"))
        req.httpMethod = "GET"
        let (data, _) = try await authReq(req)

        let json = JSONDecoder()
        let decoded = try json.decode(GetGatewayResponse.self, from: data)
        return decoded.url
    }

    public func getOwnUser() async throws -> User {
        var req = URLRequest(url: ApiClient.apiUrl.appending(path: "/users/@me"))
        req.httpMethod = "GET"
        let (data, _) = try await authReq(req)

        let json = JSONDecoder()
        let decoded = try json.decode(User.self, from: data)
        return decoded
    }

    public func createMessage(channelId: String, payload: CreateMessageReq) async throws {
        var req = URLRequest(url: ApiClient.apiUrl.appending(path: "/channels/\(channelId)/messages"))
        req.httpMethod = "POST"
        let json = JSONEncoder()

        req.httpBody = try json.encode(payload)
        try await authReq(req)
    }

    public func triggerTypingIndicator(channelId: String) async throws {
        var req = URLRequest(url: ApiClient.apiUrl.appending(path: "/channels/\(channelId)/typing"))
        req.httpMethod = "POST"
        try await authReq(req)
    }
}

public enum ApiError: Error {
    case invalidResponse
    case badStatus(_ message: String)
}
