//! Unified gRPC client for the Foxhunt API Gateway.
//!
//! All service RPCs route through a single [`FoxhuntClient`] which holds
//! one lazily-connected [`Channel`] to the API Gateway.  Individual typed
//! clients (trading, ML, risk, ...) are constructed on demand from the
//! shared channel, each wrapped with [`AuthInterceptor`] so every outgoing
//! RPC carries the JWT Bearer token when available.

use anyhow::Result;
use tonic::service::interceptor::InterceptedService;
use tonic::transport::Channel;

use crate::auth::interceptor::AuthInterceptor;
use crate::auth::token_manager::{AuthTokenManager, FileTokenStorage};

/// Channel wrapped with the JWT auth interceptor.
type AuthChannel = InterceptedService<Channel, AuthInterceptor<FileTokenStorage>>;

/// Unified gRPC client -- all service accessors share a single channel
/// and automatically inject JWT auth headers via [`AuthInterceptor`].
pub struct FoxhuntClient {
    channel: Channel,
    interceptor: AuthInterceptor<FileTokenStorage>,
}

impl FoxhuntClient {
    /// Create a client that will lazily connect to the API Gateway.
    ///
    /// Sets up the auth interceptor backed by [`FileTokenStorage`] so all
    /// outgoing RPCs include the Bearer token when one is cached on disk.
    ///
    /// The TCP handshake is deferred until the first RPC call (`connect_lazy`).
    ///
    /// # Errors
    ///
    /// Returns an error if the URL cannot be parsed or token storage init fails.
    pub async fn connect(api_url: &str) -> Result<Self> {
        let mut endpoint =
            Channel::from_shared(api_url.to_owned()).map_err(|e| anyhow::anyhow!("{e}"))?;

        // Auto-configure TLS for https endpoints.
        if api_url.starts_with("https://") {
            endpoint = endpoint
                .tls_config(tonic::transport::ClientTlsConfig::new().with_enabled_roots())
                .map_err(|e| anyhow::anyhow!("TLS config error: {e}"))?;
        }

        let channel = endpoint.connect_lazy();

        // Auth interceptor: reads cached JWT from ~/.config/foxhunt-fxt/tokens/
        // and injects "Authorization: Bearer <token>" on every RPC.
        // If no token is cached, requests proceed without auth (allows login).
        let storage = FileTokenStorage::new()?;
        let auth_manager = AuthTokenManager::new(storage);
        let interceptor = AuthInterceptor::new(auth_manager);

        Ok(Self {
            channel,
            interceptor,
        })
    }

    /// Get the underlying bare channel (no auth interceptor).
    ///
    /// Used by [`LoginClient`] which needs unauthenticated access for the
    /// initial login RPC before any token exists.
    pub fn channel(&self) -> Channel {
        self.channel.clone()
    }

    // ── Typed service accessors ──────────────────────────────────────

    /// Trading service client (order management, positions, executions).
    pub fn trading(
        &self,
    ) -> crate::proto::trading::trading_service_client::TradingServiceClient<AuthChannel> {
        crate::proto::trading::trading_service_client::TradingServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// ML inference service client (predictions, ensemble voting).
    pub fn ml(&self) -> crate::proto::ml::ml_service_client::MlServiceClient<AuthChannel> {
        crate::proto::ml::ml_service_client::MlServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// ML training service client (start/stop training, checkpoints).
    pub fn ml_training(
        &self,
    ) -> crate::proto::ml_training::ml_training_service_client::MlTrainingServiceClient<AuthChannel>
    {
        crate::proto::ml_training::ml_training_service_client::MlTrainingServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// Broker gateway service client (account info, order routing).
    pub fn broker_gateway(
        &self,
    ) -> crate::proto::broker_gateway::broker_gateway_service_client::BrokerGatewayServiceClient<AuthChannel>
    {
        crate::proto::broker_gateway::broker_gateway_service_client::BrokerGatewayServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// Trading agent service client (autonomous agent control).
    pub fn trading_agent(
        &self,
    ) -> crate::proto::trading_agent::trading_agent_service_client::TradingAgentServiceClient<AuthChannel>
    {
        crate::proto::trading_agent::trading_agent_service_client::TradingAgentServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// Monitoring service client (metrics, alerts, system health).
    pub fn monitoring(
        &self,
    ) -> crate::proto::monitoring::monitoring_service_client::MonitoringServiceClient<AuthChannel>
    {
        crate::proto::monitoring::monitoring_service_client::MonitoringServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// Configuration service client (trading service config -- package `config`).
    pub fn config(
        &self,
    ) -> crate::proto::config::config_service_client::ConfigServiceClient<AuthChannel> {
        crate::proto::config::config_service_client::ConfigServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// Data acquisition service client (market data feeds, downloads).
    pub fn data_acquisition(
        &self,
    ) -> crate::proto::data_acquisition::data_acquisition_service_client::DataAcquisitionServiceClient<AuthChannel>
    {
        crate::proto::data_acquisition::data_acquisition_service_client::DataAcquisitionServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// Risk service client (VaR, position limits, circuit breakers).
    pub fn risk(
        &self,
    ) -> crate::proto::risk::risk_service_client::RiskServiceClient<AuthChannel> {
        crate::proto::risk::risk_service_client::RiskServiceClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

    /// gRPC health check client (standard `grpc.health.v1.Health`).
    ///
    /// Health checks don't require auth, but the interceptor handles the
    /// no-token case gracefully (proceeds without Bearer header).
    pub fn health(
        &self,
    ) -> crate::proto::health::health_client::HealthClient<AuthChannel> {
        crate::proto::health::health_client::HealthClient::with_interceptor(
            self.channel.clone(),
            self.interceptor.clone(),
        )
    }

}
