//
//  BaseAPI.swift
//  iKeyMon
//
//  Created by tracer on 13.11.25.
//


import Foundation

protocol ServerAPIProtocol {
    associatedtype LoadType: Codable
    associatedtype MemoryType: Codable
    associatedtype UtilizationType: Codable
    
    func fetchSystemInfo() async throws -> SystemInfo
    func fetchLoad() async throws -> LoadType
    func fetchMemory() async throws -> MemoryType
    func fetchUtilization() async throws -> UtilizationType
    func fetchServerSummary(apiKey: String) async throws -> ServerInfo
    func restartServer(apiKey: String) async throws
}

struct SystemInfo: Codable {
    let version: String
    let timestamp: Date
    let hostname: String
}

class BaseAPIClient {
    let baseURL: URL
    let session: URLSession
    
    init(baseURL: URL, session: URLSession = .shared) {
        self.baseURL = baseURL
        self.session = session
    }
    
    func performRequest<T: Codable>(_ request: URLRequest, responseType: T.Type) async throws -> T {
        let (data, _) = try await performDataRequest(request)
        return try JSONDecoder().decode(T.self, from: data)
    }

    func performRequestWithoutBody(_ request: URLRequest) async throws {
        _ = try await performDataRequest(request)
    }

    private func performDataRequest(_ request: URLRequest) async throws -> (Data, HTTPURLResponse) {
        let (data, response) = try await session.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw APIError.invalidResponse
        }
        
        guard 200...299 ~= httpResponse.statusCode else {
            throw APIError.httpError(
                httpResponse.statusCode,
                BaseAPIClient.extractErrorMessage(from: data)
            )
        }
        
        return (data, httpResponse)
    }

    private static func extractErrorMessage(from data: Data) -> String? {
        guard !data.isEmpty else { return nil }

        if let envelope = try? JSONDecoder().decode(APIErrorEnvelope.self, from: data) {
            let parts = [envelope.code, envelope.message]
                .compactMap { $0?.trimmingCharacters(in: .whitespacesAndNewlines) }
                .filter { !$0.isEmpty }

            if !parts.isEmpty {
                return parts.joined(separator: " ")
            }
        }

        if let text = String(data: data, encoding: .utf8)?
            .trimmingCharacters(in: .whitespacesAndNewlines),
           !text.isEmpty {
            return text
        }

        return nil
    }
}

enum APIError: Error, LocalizedError {
    case invalidURL
    case invalidResponse
    case httpError(Int, String?)
    case decodingError(Error)
    case unsupportedFeature(String)
    
    var errorDescription: String? {
        switch self {
        case .invalidURL: return "Invalid URL"
        case .invalidResponse: return "Invalid response"
        case .httpError(let code, let message):
            if let message, !message.isEmpty {
                return "HTTP Error: \(code)\n\(message)"
            }
            return "HTTP Error: \(code)"
        case .decodingError(let error): return "Decoding error: \(error.localizedDescription)"
        case .unsupportedFeature(let feature): return "\(feature) is not supported by this host"
        }
    }
}

private struct APIErrorEnvelope: Decodable {
    let code: String?
    let message: String?
}
