//
//  JarvisService.swift
//  oAI
//
//  HTTP client for the Jarvis (oAI-Web) REST API.
//  Auth: Authorization: Bearer <api-key>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2026 Rune Olsen

import Foundation
import os

final class JarvisService: Sendable {
    static let shared = JarvisService()
    private init() {}

    private let log = Logger(subsystem: "com.oai.oAI", category: "jarvis")

    private var baseURL: String { SettingsService.shared.jarvisURL }
    private var apiKey: String? { SettingsService.shared.jarvisAPIKey }

    // MARK: - Agents

    func listAgents() async throws -> [JarvisAgent] {
        try await get("/api/agents")
    }

    func createAgent(_ input: JarvisAgentInput) async throws -> JarvisAgent {
        try await post("/api/agents", body: input)
    }

    func updateAgent(id: String, _ input: JarvisAgentInput) async throws -> JarvisAgent {
        try await put("/api/agents/\(id)", body: input)
    }

    func deleteAgent(id: String) async throws {
        try await voidRequest("DELETE", path: "/api/agents/\(id)")
    }

    func toggleAgent(id: String) async throws -> JarvisAgent {
        try await post("/api/agents/\(id)/toggle", body: Empty())
    }

    func runAgent(id: String) async throws {
        try await voidRequest("POST", path: "/api/agents/\(id)/run")
    }

    func stopAgent(id: String) async throws {
        try await voidRequest("POST", path: "/api/agents/\(id)/stop")
    }

    func agentRuns(id: String) async throws -> [JarvisAgentRun] {
        try await get("/api/agents/\(id)/runs")
    }

    // MARK: - Usage

    func usage() async throws -> [JarvisUsageStat] {
        let data = try await rawData("GET", path: "/api/usage")
        // API returns {summary:{...}, by_agent:[...], chat:{...}}
        if let w = try? JSONDecoder().decode(JarvisUsageResponse.self, from: data) {
            return w.byAgent ?? []
        }
        // Fallback: plain array
        if let arr = try? JSONDecoder().decode([JarvisUsageStat].self, from: data) { return arr }
        return []
    }

    func credits() async throws -> JarvisCreditsResponse {
        try await get("/api/usage/openrouter-credits")
    }

    // MARK: - Control

    func queueStatus() async throws -> JarvisQueueStatus {
        try await get("/api/status")
    }

    func pauseAll() async throws {
        try await voidRequest("POST", path: "/api/pause")
    }

    func resumeAll() async throws {
        try await voidRequest("POST", path: "/api/resume")
    }

    // MARK: - Connection test

    func testConnection() async -> Bool {
        guard !baseURL.isEmpty, let key = apiKey, !key.isEmpty else { return false }
        do {
            let _: [JarvisAgent] = try await get("/api/agents")
            return true
        } catch {
            log.warning("Jarvis connection test failed: \(error.localizedDescription)")
            return false
        }
    }

    // MARK: - HTTP core

    private func get<T: Decodable>(_ path: String) async throws -> T {
        let data = try await rawData("GET", path: path)
        return try JSONDecoder().decode(T.self, from: data)
    }

    private func post<T: Decodable, B: Encodable>(_ path: String, body: B) async throws -> T {
        let data = try await rawData("POST", path: path, body: body)
        return try JSONDecoder().decode(T.self, from: data)
    }

    private func put<T: Decodable, B: Encodable>(_ path: String, body: B) async throws -> T {
        let data = try await rawData("PUT", path: path, body: body)
        return try JSONDecoder().decode(T.self, from: data)
    }

    private func voidRequest(_ method: String, path: String) async throws {
        _ = try await rawData(method, path: path)
    }

    private func rawData<B: Encodable>(_ method: String, path: String, body: B? = nil as Empty?) async throws -> Data {
        guard let url = URL(string: baseURL.trimmingCharacters(in: .whitespaces) + path) else {
            throw JarvisError.invalidURL
        }
        guard let key = apiKey, !key.isEmpty else {
            throw JarvisError.noAPIKey
        }

        var req = URLRequest(url: url)
        req.httpMethod = method
        req.setValue("Bearer \(key)", forHTTPHeaderField: "Authorization")
        req.setValue("application/json", forHTTPHeaderField: "Accept")
        req.timeoutInterval = 30

        if let body {
            req.setValue("application/json", forHTTPHeaderField: "Content-Type")
            req.httpBody = try JSONEncoder().encode(body)
        }

        let (data, response) = try await URLSession.shared.data(for: req)

        guard let http = response as? HTTPURLResponse else {
            throw JarvisError.invalidResponse
        }
        guard (200..<300).contains(http.statusCode) else {
            let msg = (try? JSONDecoder().decode(JarvisErrBody.self, from: data))?.detail ?? "HTTP \(http.statusCode)"
            log.error("Jarvis \(method) \(path) → \(http.statusCode): \(msg)")
            throw JarvisError.serverError(http.statusCode, msg)
        }
        return data
    }
}

private struct Empty: Codable {}
private struct JarvisErrBody: Decodable { let detail: String? }
