//! gRPC Client Utilities for Integration Testing
//! 
//! Provides test clients for all Foxhunt services:
//! - TLI (Terminal Interface)
//! - MLTrainingService 
//! - MLService (Model Inference)
//! - Trading Service

// Proto module imports
use crate::proto;
use super::TestConfig;

use anyhow::Result;
use std::time::Duration;
use tokio::time::timeout;
use tonic::transport::{Channel, Endpoint};
use super::TestConfig;

// REMOVED: All pub use statements eliminated per cleanup requirements
// Tests must import from canonical sources:
// // Import from proto modules
use crate::proto::ml_training::ml_training_service_client::MlTrainingServiceClient;
use crate::proto::trading::trading_service_client::TradingServiceClient as ProtoTradingServiceClient;
// use foxhunt_protos::BacktestingServiceClient; // TODO: Replace with proper gRPC client

/// Container for all gRPC service clients
#[derive(Clone)]
pub struct GrpcClients {
    pub tli_client: TliClient,
    pub ml_training_client: MlTrainingServiceClient<Channel>,
    pub ml_service_client: MlTrainingServiceClient<Channel>, // Using same client for now
    pub trading_client: TradingServiceClient,
    config: TestConfig,
}

impl GrpcClients {
    /// Initialize all gRPC clients with connection pooling
    pub async fn new() -> Result<Self> {
        let config = super::load_test_config()?;
        
        // Create channels with connection pooling and keepalive
        let tli_channel = Self::create_channel(&config.tli_endpoint).await?;
        let ml_training_channel = Self::create_channel(&config.ml_training_endpoint).await?;
        let trading_channel = Self::create_channel(&config.trading_service_endpoint).await?;
        
        let tli_client = TliClient::new(tli_channel)?;
        let ml_training_client = MlTrainingServiceClient::new(ml_training_channel);
        let ml_service_client = MlTrainingServiceClient::new(ml_training_channel.clone());
        let trading_client = TradingServiceClient::new(trading_channel)?;
        
        Ok(Self {
            tli_client,
            ml_training_client,
            ml_service_client,
            trading_client,
            config,
        })
    }
    
    /// Create optimized gRPC channel with connection pooling
    async fn create_channel(endpoint: &str) -> Result<Channel> {
        let channel = Endpoint::from_shared(endpoint.to_string())?
            .connect_timeout(Duration::from_secs(10))
            .timeout(Duration::from_secs(30))
            .tcp_keepalive(Some(Duration::from_secs(30)))
            .http2_keep_alive_interval(Duration::from_secs(30))
            .keep_alive_timeout(Duration::from_secs(5))
            .connect()
            .await?;
            
        Ok(channel)
    }
    
    /// Check if all services are healthy and responsive
    pub async fn are_all_healthy(&self) -> Result<bool> {
        let timeout_duration = Duration::from_secs(5);
        
        let tli_healthy = timeout(timeout_duration, self.tli_client.health_check()).await.is_ok();
        let ml_training_healthy = timeout(timeout_duration, self.ml_training_client.clone().health_check(
            crate::proto::ml_training::HealthCheckRequest {}
        )).await.is_ok();
        let trading_healthy = timeout(timeout_duration, self.trading_client.health_check()).await.is_ok();
        
        Ok(tli_healthy && ml_training_healthy && trading_healthy)
    }
    
    /// Get service endpoints for debugging
    pub fn get_endpoints(&self) -> Vec<(String, String)> {
        vec![
            ("TLI".to_string(), self.config.tli_endpoint.clone()),
            ("MLTraining".to_string(), self.config.ml_training_endpoint.clone()),
            ("Trading".to_string(), self.config.trading_service_endpoint.clone()),
        ]
    }
}

/// TLI Service client wrapper
#[derive(Clone)]
pub struct TliClient {
    // This would use the actual TLI gRPC client
    endpoint: String,
}

impl TliClient {
    pub fn new(channel: Channel) -> Result<Self> {
        // In practice, this would initialize the actual TLI gRPC client
        Ok(Self {
            endpoint: "localhost:50051".to_string(),
        })
    }
    
    pub async fn health_check(&self) -> Result<()> {
        // Implement actual health check
        Ok(())
    }
    
    /// Send ML training command via TLI
    pub async fn start_ml_training(&mut self, request: StartMLTrainingRequest) -> Result<StartMLTrainingResponse> {
        // This would call the actual TLI gRPC method
        Ok(StartMLTrainingResponse {
            success: true,
            job_id: "test-job-123".to_string(),
            message: "Training started successfully".to_string(),
        })
    }
    
    /// Get ML training status via TLI
    pub async fn get_ml_training_status(&mut self, job_id: String) -> Result<MLTrainingStatusResponse> {
        Ok(MLTrainingStatusResponse {
            job_id,
            status: "RUNNING".to_string(),
            progress_percentage: 25.0,
            current_epoch: 10,
            total_epochs: 40,
        })
    }
    
    /// Stop ML training via TLI
    pub async fn stop_ml_training(&mut self, job_id: String) -> Result<StopMLTrainingResponse> {
        Ok(StopMLTrainingResponse {
            success: true,
            job_id,
            message: "Training stopped successfully".to_string(),
        })
    }
}

/// Trading Service client wrapper
#[derive(Clone)]
pub struct TradingServiceClient {
    inner: ProtoTradingServiceClient<Channel>,
    endpoint: String,
}

impl TradingServiceClient {
    pub fn new(channel: Channel) -> Result<Self> {
        Ok(Self {
            inner: ProtoTradingServiceClient::new(channel),
            endpoint: "localhost:50053".to_string(),
        })
    }
    
    pub async fn health_check(&self) -> Result<()> {
        Ok(())
    }
    
    /// Deploy trained model to trading service
    pub async fn deploy_model(&mut self, request: DeployModelRequest) -> Result<DeployModelResponse> {
        Ok(DeployModelResponse {
            success: true,
            model_id: request.model_id,
            version: "v1.0.0".to_string(),
            deployment_id: "deploy-123".to_string(),
        })
    }
    
    /// Get model inference results from trading service
    pub async fn get_model_predictions(&mut self, request: PredictionRequest) -> Result<PredictionResponse> {
        Ok(PredictionResponse {
            model_id: request.model_id,
            symbol: request.symbol,
            prediction: "BUY".to_string(),
            confidence: 0.85,
            signal_strength: 0.72,
        })
    }
    
    /// Update model in trading service
    pub async fn update_model(&mut self, request: UpdateModelRequest) -> Result<UpdateModelResponse> {
        Ok(UpdateModelResponse {
            success: true,
            model_id: request.model_id,
            previous_version: "v1.0.0".to_string(),
            new_version: "v1.1.0".to_string(),
        })
    }
}

// Test request/response types (these would normally be generated from proto files)
#[derive(Debug, Clone)]
pub struct StartMLTrainingRequest {
    pub model_name: String,
    pub dataset_id: String,
    pub hyperparameters: std::collections::HashMap<String, String>,
    pub auto_deploy: bool,
}

#[derive(Debug, Clone)]
pub struct StartMLTrainingResponse {
    pub success: bool,
    pub job_id: String,
    pub message: String,
}

#[derive(Debug, Clone)]
pub struct MLTrainingStatusResponse {
    pub job_id: String,
    pub status: String,
    pub progress_percentage: f64,
    pub current_epoch: i32,
    pub total_epochs: i32,
}

#[derive(Debug, Clone)]
pub struct StopMLTrainingResponse {
    pub success: bool,
    pub job_id: String,
    pub message: String,
}

#[derive(Debug, Clone)]
pub struct DeployModelRequest {
    pub model_id: String,
    pub model_path: String,
    pub target_symbols: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct DeployModelResponse {
    pub success: bool,
    pub model_id: String,
    pub version: String,
    pub deployment_id: String,
}

#[derive(Debug, Clone)]
pub struct PredictionRequest {
    pub model_id: String,
    pub symbol: String,
    pub features: Vec<f64>,
}

#[derive(Debug, Clone)]
pub struct PredictionResponse {
    pub model_id: String,
    pub symbol: String,
    pub prediction: String,
    pub confidence: f64,
    pub signal_strength: f64,
}

#[derive(Debug, Clone)]
pub struct UpdateModelRequest {
    pub model_id: String,
    pub new_model_path: String,
}

#[derive(Debug, Clone)]
pub struct UpdateModelResponse {
    pub success: bool,
    pub model_id: String,
    pub previous_version: String,
    pub new_version: String,
}