//
//  JarvisModels.swift
//  oAI
//
//  Data models for the Jarvis (oAI-Web) API integration.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2026 Rune Olsen

import Foundation

// MARK: - Agent

struct JarvisAgent: Identifiable, Codable, Hashable, Sendable {
    let id: String
    var name: String
    var description: String
    var prompt: String
    var model: String
    var enabled: Bool
    var schedule: String?
    var canCreateSubagents: Bool
    var allowedTools: [String]
    var maxToolCalls: Int?
    var promptMode: String
    let createdAt: String?
    var isRunning: Bool?
    var lastRunAt: String?
    var lastRunStatus: String?

    enum CodingKeys: String, CodingKey {
        case id, name, description, prompt, model, enabled, schedule
        case canCreateSubagents   = "can_create_subagents"
        case allowedTools         = "allowed_tools"
        case maxToolCalls         = "max_tool_calls"
        case promptMode           = "prompt_mode"
        case createdAt            = "created_at"
        case isRunning            = "is_running"
        case lastRunAt            = "last_run_at"
        case lastRunStatus        = "last_run_status"
    }
}

// MARK: - Agent Input (create / update)

struct JarvisAgentInput: Codable, Sendable {
    var name: String
    var prompt: String
    var model: String
    var description: String      = ""
    var enabled: Bool            = true
    var schedule: String?        = nil
    var canCreateSubagents: Bool = false
    var allowedTools: [String]   = []
    var maxToolCalls: Int?       = nil
    var promptMode: String       = "combined"

    enum CodingKeys: String, CodingKey {
        case name, prompt, model, description, enabled, schedule
        case canCreateSubagents = "can_create_subagents"
        case allowedTools       = "allowed_tools"
        case maxToolCalls       = "max_tool_calls"
        case promptMode         = "prompt_mode"
    }
}

// MARK: - Agent Run

struct JarvisAgentRun: Identifiable, Codable, Sendable {
    let id: String
    let agentId: String?
    let status: String        // "running" | "completed" | "failed" | "stopped"
    let startedAt: String?
    let finishedAt: String?
    let output: String?
    let error: String?
    let costUsd: Double?
    let inputTokens: Int?
    let outputTokens: Int?
    let triggerType: String?

    enum CodingKeys: String, CodingKey {
        case id, status, output, error
        case agentId       = "agent_id"
        case startedAt     = "started_at"
        case finishedAt    = "finished_at"
        case costUsd       = "cost_usd"
        case inputTokens   = "input_tokens"
        case outputTokens  = "output_tokens"
        case triggerType   = "trigger_type"
    }

    var isActive: Bool { status == "running" }

    var totalTokens: Int { (inputTokens ?? 0) + (outputTokens ?? 0) }

    var formattedStarted: String {
        guard let s = startedAt else { return "—" }
        return isoFormatter.string(from: isoParser.date(from: s) ?? Date())
    }

    var formattedDuration: String? {
        guard let s = startedAt, let f = finishedAt,
              let sd = isoParser.date(from: s), let fd = isoParser.date(from: f) else { return nil }
        let secs = Int(fd.timeIntervalSince(sd))
        if secs < 60 { return "\(secs)s" }
        return "\(secs / 60)m \(secs % 60)s"
    }
}

private let isoParser: ISO8601DateFormatter = {
    let f = ISO8601DateFormatter()
    f.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
    return f
}()

private let isoFormatter: DateFormatter = {
    let f = DateFormatter()
    f.dateStyle = .short
    f.timeStyle = .short
    return f
}()

// MARK: - Usage

struct JarvisUsageStat: Identifiable, Codable, Sendable {
    let agentId: String?
    let agentName: String?
    let model: String?
    let runCount: Int?
    let totalInputTokens: Int?
    let totalOutputTokens: Int?
    let totalCostUsd: Double?

    var id: String { agentId ?? agentName ?? "unknown" }
    var totalTokens: Int { (totalInputTokens ?? 0) + (totalOutputTokens ?? 0) }
    var displayName: String { agentName ?? agentId ?? "Unknown" }

    enum CodingKeys: String, CodingKey {
        case agentId           = "agent_id"
        case agentName         = "agent_name"
        case model
        case runCount          = "runs"
        case totalInputTokens  = "input_tokens"
        case totalOutputTokens = "output_tokens"
        case totalCostUsd      = "cost_usd"
    }
}

// MARK: - Usage Response (top-level wrapper)

struct JarvisUsageResponse: Decodable, Sendable {
    let summary: JarvisUsageSummary?
    let byAgent: [JarvisUsageStat]?

    enum CodingKeys: String, CodingKey {
        case summary
        case byAgent = "by_agent"
    }
}

struct JarvisUsageSummary: Codable, Sendable {
    let runs: Int?
    let inputTokens: Int?
    let outputTokens: Int?
    let costUsd: Double?

    enum CodingKeys: String, CodingKey {
        case runs
        case inputTokens  = "input_tokens"
        case outputTokens = "output_tokens"
        case costUsd      = "cost_usd"
    }
}

// MARK: - Credits

struct JarvisCreditsResponse: Codable, Sendable {
    let totalCredits: Double?
    let totalUsage: Double?
    let balance: Double?

    enum CodingKeys: String, CodingKey {
        case totalCredits = "total_credits"
        case totalUsage   = "total_usage"
        case balance
    }

    var remainingBalance: Double? {
        if let b = balance { return b }
        if let c = totalCredits, let u = totalUsage { return c - u }
        return nil
    }
}

// MARK: - Queue / System status

struct JarvisQueueStatus: Codable, Sendable {
    let paused: Bool?
    let queueLength: Int?
    let runningCount: Int?

    enum CodingKeys: String, CodingKey {
        case paused
        case queueLength  = "queue_length"
        case runningCount = "running_count"
    }
}

// MARK: - Errors

enum JarvisError: LocalizedError {
    case invalidURL
    case noAPIKey
    case invalidResponse
    case serverError(Int, String)

    var errorDescription: String? {
        switch self {
        case .invalidURL:               return "Invalid Jarvis URL"
        case .noAPIKey:                 return "No API key configured — add one in Settings → Jarvis"
        case .invalidResponse:          return "Invalid server response"
        case .serverError(let c, let m): return "Server error \(c): \(m)"
        }
    }
}
