/// Recursive JSON value type for decoding arbitrary server payloads.
///
/// Used for tool args and any untyped JSON from the server.
/// Replaces third-party `AnyCodable` — stdlib has no recursive Codable JSON type.
enum JSONValue: Sendable, Equatable, Hashable {
    case string(String)
    case number(Double)
    case bool(Bool)
    case null
    case array([Self])
    case object([String: Self])
}

// MARK: - Codable

extension JSONValue: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if container.decodeNil() {
            self = .null
        } else if let bool = try? container.decode(Bool.self) {
            self = .bool(bool)
        } else if let number = try? container.decode(Double.self) {
            self = .number(number)
        } else if let string = try? container.decode(String.self) {
            self = .string(string)
        } else if let array = try? container.decode([JSONValue].self) {
            self = .array(array)
        } else if let object = try? container.decode([String: JSONValue].self) {
            self = .object(object)
        } else {
            throw DecodingError.typeMismatch(
                JSONValue.self,
                .init(codingPath: decoder.codingPath, debugDescription: "Unsupported JSON type")
            )
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .string(let s): try container.encode(s)
        case .number(let n): try container.encode(n)
        case .bool(let b): try container.encode(b)
        case .null: try container.encodeNil()
        case .array(let a): try container.encode(a)
        case .object(let o): try container.encode(o)
        }
    }
}

// MARK: - Convenience

extension JSONValue {
    /// Extract string value, or nil.
    var stringValue: String? {
        if case .string(let s) = self { return s }
        return nil
    }

    /// Extract double value, or nil.
    var numberValue: Double? {
        if case .number(let n) = self { return n }
        return nil
    }

    /// Extract bool value, or nil.
    var boolValue: Bool? {
        if case .bool(let b) = self { return b }
        return nil
    }

    /// Extract object value, or nil.
    var objectValue: [String: JSONValue]? {
        if case .object(let o) = self { return o }
        return nil
    }

    /// Extract array value, or nil.
    var arrayValue: [JSONValue]? {
        if case .array(let a) = self { return a }
        return nil
    }

    /// One-line summary for tool args display. Truncated to `maxLength`.
    /// Human-readable display for answer values.
    /// Strings pass through, arrays join with ", ", others use summary().
    var displayString: String {
        switch self {
        case .string(let s): return s
        case .array(let a):
            return a.map(\.displayString).joined(separator: ", ")
        default:
            return summary()
        }
    }

    func summary(maxLength: Int = 80) -> String {
        let raw: String
        switch self {
        case .string(let s): raw = s
        case .number(let n): raw = n.truncatingRemainder(dividingBy: 1) == 0 ? String(Int(n)) : String(n)
        case .bool(let b): raw = String(b)
        case .null: raw = "null"
        case .array(let a): raw = "[\(a.count) items]"
        case .object(let o): raw = "{\(o.count) keys}"
        }
        // Fast path: UTF-8 byte count ≥ character count. For ASCII text,
        // utf8.count == character count, avoiding O(n) grapheme iteration.
        if raw.utf8.count <= maxLength { return raw }
        if raw.count <= maxLength { return raw }
        return String(raw.prefix(maxLength - 1)) + "…"
    }
}

// MARK: - ExpressibleBy Literals

extension JSONValue: ExpressibleByStringLiteral {
    init(stringLiteral value: String) { self = .string(value) }
}

extension JSONValue: ExpressibleByIntegerLiteral {
    init(integerLiteral value: Int) { self = .number(Double(value)) }
}

extension JSONValue: ExpressibleByFloatLiteral {
    init(floatLiteral value: Double) { self = .number(value) }
}

extension JSONValue: ExpressibleByBooleanLiteral {
    init(booleanLiteral value: Bool) { self = .bool(value) }
}

extension JSONValue: ExpressibleByNilLiteral {
    init(nilLiteral: ()) { self = .null }
}

extension JSONValue: ExpressibleByArrayLiteral {
    init(arrayLiteral elements: JSONValue...) { self = .array(elements) }
}

extension JSONValue: ExpressibleByDictionaryLiteral {
    init(dictionaryLiteral elements: (String, JSONValue)...) {
        self = .object(Dictionary(uniqueKeysWithValues: elements))
    }
}
