import Foundation

/// Newline-delimited JSON protocol handler.
/// Matches Python: protocol.py build_request, build_response, parse_message, encode_binary, decode_binary
enum ProtocolHandler {

    /// Build a request message (newline-terminated JSON).
    /// Matches Python: build_request(msg_type, request_id=None, **kwargs)
    static func buildRequest(type: String, requestId: String? = nil, params: [String: Any] = [:]) throws -> Data {
        var msg: [String: Any] = ["type": type]
        if let requestId = requestId {
            msg["request_id"] = requestId
        }
        // Merge params into msg
        for (key, value) in params {
            msg[key] = value
        }

        let jsonData = try JSONSerialization.data(withJSONObject: msg)
        guard jsonData.count < Constants.maxMessageBytes else {
            throw NetworkError.messageTooLarge
        }
        return jsonData + Data([0x0A])  // newline
    }

    /// Build a response message (newline-terminated JSON).
    static func buildResponse(type: String, status: String, data: [String: Any]? = nil, requestId: String? = nil) throws -> Data {
        var msg: [String: Any] = ["type": type, "status": status]
        if let data = data {
            msg["data"] = data
        }
        if let requestId = requestId {
            msg["request_id"] = requestId
        }

        let jsonData = try JSONSerialization.data(withJSONObject: msg)
        guard jsonData.count < Constants.maxMessageBytes else {
            throw NetworkError.messageTooLarge
        }
        return jsonData + Data([0x0A])
    }

    /// Parse a single protocol message from bytes.
    /// Matches Python: parse_message(line)
    static func parseMessage(_ data: Data) throws -> [String: Any] {
        let trimmed = data.trimmingNewlines()
        guard !trimmed.isEmpty else {
            throw NetworkError.protocolError("Empty message")
        }
        guard let obj = try JSONSerialization.jsonObject(with: trimmed) as? [String: Any] else {
            throw NetworkError.protocolError("Message is not a JSON object")
        }
        return obj
    }

    /// Encode bytes to base64 string.
    /// Matches Python: encode_binary(data)
    static func encodeBinary(_ data: Data) -> String {
        data.base64EncodedString(options: [])
    }

    /// Decode base64 string to bytes.
    /// Matches Python: decode_binary(data)
    static func decodeBinary(_ string: String) throws -> Data {
        guard let data = Data(base64Encoded: string, options: .ignoreUnknownCharacters) else {
            throw CryptoError.invalidBase64
        }
        return data
    }

    /// Generate a new request ID (UUID string).
    static func newRequestId() -> String {
        UUID().uuidString
    }
}

// MARK: - Data Helpers

private extension Data {
    func trimmingNewlines() -> Data {
        var data = self
        while let last = data.last, last == 0x0A || last == 0x0D {
            data.removeLast()
        }
        while let first = data.first, first == 0x0A || first == 0x0D {
            data.removeFirst()
        }
        return data
    }
}
