// CentrifugoClient.swift
// Generated Centrifugo RPC Client
// Auto-generated - DO NOT EDIT
// Generated at: {{ generated_at }}
// API Version: {{ api_version }}

import Foundation

/// Generated client for Centrifugo RPC methods.
///
/// This client provides type-safe access to all RPC methods defined on the server.
/// It wraps a `CentrifugoRPCClient` and provides strongly-typed method signatures.
///
/// Usage:
/// ```swift
/// let rpc = try CentrifugoRPCClient(url: "...", token: "...", userId: "...")
/// let client = CentrifugoClient(rpc: rpc)
/// try await client.connect()
///
/// let result = try await client.terminalInput(params: TerminalInputParams(...))
///
/// await client.disconnect()
/// ```
public actor CentrifugoClient {
    // MARK: - Properties

    private let rpc: CentrifugoRPCClient

    /// API contract version hash.
    /// Changes when the API contract (methods or types) changes.
    public static let apiVersion = "{{ api_version }}"

    // MARK: - Initialization

    /// Create a new Centrifugo client.
    ///
    /// - Parameter rpc: The underlying RPC client to use for communication
    public init(rpc: CentrifugoRPCClient) {
        self.rpc = rpc
    }

    /// Create a new Centrifugo client with connection parameters.
    ///
    /// - Parameters:
    ///   - url: WebSocket URL of the Centrifugo server
    ///   - token: JWT authentication token
    ///   - userId: User ID for the reply channel
    ///   - timeout: RPC call timeout in seconds (default: 30)
    /// - Throws: URLError if the URL is invalid
    public init(
        url: String,
        token: String,
        userId: String,
        timeout: TimeInterval = 30
    ) throws {
        self.rpc = try CentrifugoRPCClient(
            url: url,
            token: token,
            userId: userId,
            timeout: timeout
        )
    }

    // MARK: - Connection Management

    /// Connect to the Centrifugo server.
    public func connect() async throws {
        try await rpc.connect()
    }

    /// Disconnect from the Centrifugo server.
    public func disconnect() async {
        await rpc.disconnect()
    }

    /// Current connection state.
    public var connectionState: CentrifugoConnectionState {
        get async { await rpc.state }
    }

    // MARK: - Generated RPC Methods

{% for method in methods %}
    /// {{ method.docstring }}
{% if method.no_wait %}
    ///
    /// This is a fire-and-forget method - no response is expected.
    ///
    /// - Parameter params: Request parameters
    /// - Throws: RPCError if sending fails
    public func {{ method.swift_name }}(params: {{ method.param_type }}) async throws {
        try await rpc.send(method: "{{ method.rpc_name }}", params: params)
    }
{% else %}
    ///
    /// - Parameters:
    ///   - params: Request parameters
    ///   - timeout: Optional timeout in seconds (defaults to client's default)
    /// - Returns: {{ method.return_type }}
    /// - Throws: RPCError if the call fails or times out
    public func {{ method.swift_name }}(
        params: {{ method.param_type }},
        timeout: TimeInterval? = nil
    ) async throws -> {{ method.return_type }} {
        return try await rpc.call(method: "{{ method.rpc_name }}", params: params, timeout: timeout)
    }
{% endif %}

{% endfor %}
{% if channels %}

    // MARK: - Channel Subscriptions

{% for channel in channels %}
    /// {{ channel.docstring }}
    ///
    /// Channel pattern: `{{ channel.pattern }}`
    ///
{% for param in channel.params %}
    /// - Parameter {{ param }}: {{ param | replace("_", " ") | capitalize }}
{% endfor %}
    /// - Parameter onEvent: Callback for channel events
    /// - Throws: RPCError if subscription fails
{% if channel.params | length == 1 %}
    public func subscribeTo{{ channel.swift_name | capitalize }}(
        {{ channel.params[0] }}: String,
        onEvent: @escaping ({{ channel.event_enum }}) -> Void
    ) async throws {
        let channel = "{{ channel.pattern }}".replacingOccurrences(of: "{{ '{' }}{{ channel.params[0] }}{{ '}' }}", with: {{ channel.params[0] }})
        try await rpc.subscribe(channel: channel) { data in
            do {
                let event = try JSONDecoder().decode({{ channel.event_enum }}.self, from: data)
                onEvent(event)
            } catch {
                // Log decoding error for debugging
                let rawString = String(data: data, encoding: .utf8) ?? "<binary data>"
                print("[Centrifugo] Failed to decode {{ channel.event_enum }}: \(error)")
                print("[Centrifugo] Raw data: \(rawString.prefix(500))")
            }
        }
    }

    /// Unsubscribe from {{ channel.name }} channel.
    ///
    /// - Parameter {{ channel.params[0] }}: {{ channel.params[0] | replace("_", " ") | capitalize }}
    public func unsubscribeFrom{{ channel.swift_name | capitalize }}({{ channel.params[0] }}: String) async throws {
        let channel = "{{ channel.pattern }}".replacingOccurrences(of: "{{ '{' }}{{ channel.params[0] }}{{ '}' }}", with: {{ channel.params[0] }})
        try await rpc.unsubscribe(channel: channel)
    }

{% else %}
    public func subscribeTo{{ channel.swift_name | capitalize }}(
{% for param in channel.params %}
        {{ param }}: String{% if not loop.last %},{% endif %}

{% endfor %}
        onEvent: @escaping ({{ channel.event_enum }}) -> Void
    ) async throws {
        var channel = "{{ channel.pattern }}"
{% for param in channel.params %}
        channel = channel.replacingOccurrences(of: "{{ '{' }}{{ param }}{{ '}' }}", with: {{ param }})
{% endfor %}
        try await rpc.subscribe(channel: channel) { data in
            do {
                let event = try JSONDecoder().decode({{ channel.event_enum }}.self, from: data)
                onEvent(event)
            } catch {
                // Log decoding error for debugging
                let rawString = String(data: data, encoding: .utf8) ?? "<binary data>"
                print("[Centrifugo] Failed to decode {{ channel.event_enum }}: \(error)")
                print("[Centrifugo] Raw data: \(rawString.prefix(500))")
            }
        }
    }

    /// Unsubscribe from {{ channel.name }} channel.
    public func unsubscribeFrom{{ channel.swift_name | capitalize }}(
{% for param in channel.params %}
        {{ param }}: String{% if not loop.last %},{% endif %}

{% endfor %}
    ) async throws {
        var channel = "{{ channel.pattern }}"
{% for param in channel.params %}
        channel = channel.replacingOccurrences(of: "{{ '{' }}{{ param }}{{ '}' }}", with: {{ param }})
{% endfor %}
        try await rpc.unsubscribe(channel: channel)
    }

{% endif %}
{% endfor %}
{% endif %}
}

// MARK: - Convenience Extensions

extension CentrifugoClient {
    /// Check if connected to the server.
    public var isConnected: Bool {
        get async {
            await connectionState == .connected
        }
    }

    /// Make a raw RPC call with custom types.
    ///
    /// Use this for RPC methods not covered by generated type-safe methods,
    /// or when you need custom timeout handling.
    ///
    /// - Parameters:
    ///   - method: RPC method name (e.g., "agent.run_agent")
    ///   - params: Request parameters (must be Encodable)
    ///   - timeout: Optional timeout in seconds (defaults to client's default)
    /// - Returns: Response decoded as the specified type
    /// - Throws: RPCError if the call fails or times out
    public func rawRPCCall<P: Encodable, R: Decodable>(
        method: String,
        params: P,
        timeout: TimeInterval? = nil
    ) async throws -> R {
        return try await rpc.call(method: method, params: params, timeout: timeout)
    }
}
