#![deny(clippy::unwrap_used, clippy::expect_used)]
//! Broker Gateway Service - Main Entry Point
//!
//! gRPC server for routing orders to AMP Futures broker via FIX protocol.
//! MVP implementation with database persistence (FIX protocol deferred to Phase 2).

use anyhow::{Context, Result};
use broker_gateway_service::proto::broker_gateway::broker_gateway_service_server::BrokerGatewayServiceServer;
use broker_gateway_service::service::BrokerGatewayService;
use sqlx::PgPool;
use tokio::signal;
use tonic::transport::Server;
use tracing::{error, info, warn};

use common::metrics::GrpcMetricsLayer;

// Service configuration
const DEFAULT_GRPC_PORT: u16 = 50056;
const DEFAULT_HEALTH_PORT: u16 = 8086;
const DEFAULT_METRICS_PORT: u16 = 9096;

#[tokio::main]
async fn main() -> Result<()> {
    // Initialize observability (JSON logging + OpenTelemetry tracing via OTLP)
    let otlp_endpoint = std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT")
        .unwrap_or_else(|_| "http://localhost:4317".to_string());
    if let Err(e) = common::observability::init_observability("broker_gateway", Some(&otlp_endpoint)) {
        eprintln!("Failed to initialize observability: {}", e);
    }

    info!("🚀 Starting Foxhunt Broker Gateway Service...");

    // Get database URL from environment
    let database_url = std::env::var("DATABASE_URL")
        .context("DATABASE_URL environment variable must be set")?;

    // Initialize database pool
    let db_pool = PgPool::connect(&database_url)
        .await
        .context("Failed to connect to database")?;

    info!("✓ Database connection established");

    // Get Redis URL from environment
    let redis_url = std::env::var("REDIS_URL")
        .unwrap_or_else(|_| "redis://localhost:6379".to_string());

    // Initialize service
    let service = BrokerGatewayService::new(db_pool.clone(), &redis_url)
        .context("Failed to initialize Broker Gateway Service")?;

    // Optionally connect cTrader broker
    {
        let broker_enabled = std::env::var("CTRADER_ENABLED")
            .map(|v| v == "true" || v == "1")
            .unwrap_or(false);

        if broker_enabled {
            info!("cTrader broker connection enabled — connecting...");
            let ct_config = ctrader_openapi::CTraderConfig {
                client_id: std::env::var("CTRADER_CLIENT_ID")
                    .context("CTRADER_CLIENT_ID required when CTRADER_ENABLED=true")?,
                client_secret: std::env::var("CTRADER_CLIENT_SECRET")
                    .context("CTRADER_CLIENT_SECRET required when CTRADER_ENABLED=true")?,
                access_token: std::env::var("CTRADER_ACCESS_TOKEN")
                    .context("CTRADER_ACCESS_TOKEN required when CTRADER_ENABLED=true")?,
                account_id: std::env::var("CTRADER_ACCOUNT_ID")
                    .context("CTRADER_ACCOUNT_ID required")?
                    .parse()
                    .context("CTRADER_ACCOUNT_ID must be a number")?,
                environment: if std::env::var("CTRADER_LIVE")
                    .map(|v| v == "true")
                    .unwrap_or(false)
                {
                    // Require explicit confirmation to prevent accidental live trading
                    let confirmed = std::env::var("CTRADER_LIVE_CONFIRMED")
                        .unwrap_or_default();
                    if confirmed != "I_UNDERSTAND_REAL_MONEY" {
                        anyhow::bail!(
                            "CTRADER_LIVE=true requires CTRADER_LIVE_CONFIRMED=I_UNDERSTAND_REAL_MONEY \
                             to prevent accidental live trading. Set both env vars to proceed."
                        );
                    }
                    warn!("*** LIVE TRADING MODE ACTIVATED — real money orders will be submitted to cTrader ***");
                    ctrader_openapi::CTraderEnvironment::Live
                } else {
                    info!("cTrader running in DEMO mode (set CTRADER_LIVE=true to enable live trading)");
                    ctrader_openapi::CTraderEnvironment::Demo
                },
                heartbeat_interval_secs: std::env::var("CTRADER_HEARTBEAT_SECS")
                    .ok()
                    .and_then(|v| v.parse().ok())
                    .unwrap_or(10),
                request_timeout_ms: std::env::var("CTRADER_TIMEOUT_MS")
                    .ok()
                    .and_then(|v| v.parse().ok())
                    .unwrap_or(5000),
                max_reconnect_attempts: std::env::var("CTRADER_MAX_RECONNECT")
                    .ok()
                    .and_then(|v| v.parse().ok())
                    .unwrap_or(5),
            };

            match ctrader_openapi::CTraderClient::connect(ct_config).await {
                Ok(ct_client) => {
                    service.set_broker_client(ct_client).await;
                    info!("✓ cTrader broker connected");
                }
                Err(e) => {
                    error!(
                        "Failed to connect cTrader broker: {} — running without live broker",
                        e
                    );
                }
            }
        } else {
            info!("cTrader broker disabled (set CTRADER_ENABLED=true to enable)");
        }
    }

    info!("✓ Broker Gateway Service initialized");

    // Create health service
    let (health_reporter, health_service) = tonic_health::server::health_reporter();
    health_reporter
        .set_serving::<BrokerGatewayServiceServer<BrokerGatewayService>>()
        .await;

    info!("✓ Health service initialized");

    // Get gRPC port from environment
    let grpc_port = std::env::var("GRPC_PORT")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(DEFAULT_GRPC_PORT);
    let addr = format!("0.0.0.0:{}", grpc_port).parse()?;

    info!("🔧 gRPC server configuration:");
    info!("   - Port: {}", grpc_port);
    info!("   - Health port: {}", DEFAULT_HEALTH_PORT);
    info!("   - Metrics port: {}", DEFAULT_METRICS_PORT);
    info!("   - TLS: disabled (development mode)");
    info!("   - Authentication: disabled (internal service)");

    // Build gRPC server
    let server = Server::builder()
        .layer(GrpcMetricsLayer::new("broker-gateway"))
        .add_service(health_service)
        .add_service(BrokerGatewayServiceServer::new(service))
        .serve_with_shutdown(addr, shutdown_signal());

    info!("✅ Broker Gateway Service listening on {}", addr);
    info!("📋 Service endpoints:");
    info!("   - gRPC: 0.0.0.0:{}", grpc_port);
    info!("   - Health: http://0.0.0.0:{}/health", DEFAULT_HEALTH_PORT);
    info!("   - Metrics: http://0.0.0.0:{}/metrics", DEFAULT_METRICS_PORT);
    info!("");
    info!("   Set CTRADER_ENABLED=true with credentials for live broker routing");

    // Start background tasks
    tokio::select! {
        result = server => {
            if let Err(e) = result {
                error!("gRPC server error: {}", e);
            }
        }
        _ = start_health_endpoint(DEFAULT_HEALTH_PORT, db_pool.clone()) => {
            warn!("Health endpoint stopped");
        }
        _ = start_metrics_endpoint(DEFAULT_METRICS_PORT) => {
            warn!("Metrics endpoint stopped");
        }
    }

    info!("Broker Gateway Service shutdown complete");
    Ok(())
}

/// Start Prometheus metrics HTTP endpoint
async fn start_metrics_endpoint(port: u16) -> Result<()> {
    use axum::{routing::get, Router};

    let app = Router::new().route("/metrics", get(metrics_handler));

    let addr: std::net::SocketAddr = ([0, 0, 0, 0], port).into();
    let listener = tokio::net::TcpListener::bind(addr)
        .await
        .context("Failed to bind metrics endpoint")?;

    info!("✓ Metrics endpoint listening on http://{}/metrics", addr);

    axum::serve(listener, app)
        .await
        .context("Metrics server error")?;

    Ok(())
}

/// Metrics handler for Prometheus scraping
async fn metrics_handler() -> String {
    use prometheus::{Encoder, TextEncoder};

    let encoder = TextEncoder::new();
    let metric_families = prometheus::gather();
    let mut buffer = vec![];
    if let Err(e) = encoder.encode(&metric_families, &mut buffer) {
        return format!("# Error encoding metrics: {e}");
    }
    String::from_utf8(buffer).unwrap_or_else(|_| "# Error: metrics contain invalid UTF-8".to_string())
}

/// Start health check HTTP endpoint
async fn start_health_endpoint(port: u16, db_pool: PgPool) -> Result<()> {
    use hyper::server::conn::http1;
    use hyper::service::service_fn;
    use hyper_util::rt::TokioIo;
    use tokio::net::TcpListener;

    let addr: std::net::SocketAddr = ([0, 0, 0, 0], port).into();
    let listener = TcpListener::bind(addr)
        .await
        .context("Failed to bind health endpoint")?;

    info!("✓ Health endpoint listening on http://{}", addr);

    loop {
        let (stream, _) = match listener.accept().await {
            Ok(conn) => conn,
            Err(e) => {
                error!("Failed to accept connection: {}", e);
                continue;
            }
        };

        let db_pool_clone = db_pool.clone();

        tokio::spawn(async move {
            let io = TokioIo::new(stream);
            if let Err(e) = http1::Builder::new()
                .serve_connection(
                    io,
                    service_fn(move |req| health_handler(req, db_pool_clone.clone())),
                )
                .await
            {
                error!("Health server error: {}", e);
            }
        });
    }
}

/// Health check handler
async fn health_handler(
    _req: hyper::Request<hyper::body::Incoming>,
    db_pool: PgPool,
) -> Result<hyper::Response<http_body_util::Full<bytes::Bytes>>, std::convert::Infallible> {
    use bytes::Bytes;
    use http_body_util::Full;

    // Check database connection
    let db_healthy = sqlx::query("SELECT 1")
        .fetch_optional(&db_pool)
        .await
        .is_ok();

    let status_code = if db_healthy { 200 } else { 503 };

    let health_response = serde_json::json!({
        "status": if db_healthy { "healthy" } else { "degraded" },
        "service": "broker_gateway_service",
        "timestamp": chrono::Utc::now().to_rfc3339(),
        "version": env!("CARGO_PKG_VERSION"),
        "checks": {
            "database": db_healthy,
            "fix_session": "not_implemented",
        },
        "mvp_mode": true,
    });

    let response = hyper::Response::builder()
        .status(status_code)
        .header("content-type", "application/json")
        .body(Full::new(Bytes::from(health_response.to_string())))
        .unwrap_or_else(|e| {
            error!("Failed to build health response: {}", e);
            hyper::Response::new(Full::new(Bytes::from(
                r#"{"status":"error","message":"Health check failed"}"#,
            )))
        });

    Ok(response)
}

/// Handle shutdown signals
async fn shutdown_signal() {
    let ctrl_c = async {
        if let Err(e) = signal::ctrl_c().await {
            error!("Failed to install Ctrl+C handler: {}", e);
        }
    };

    #[cfg(unix)]
    let terminate = async {
        match signal::unix::signal(signal::unix::SignalKind::terminate()) {
            Ok(mut signal_stream) => {
                signal_stream.recv().await;
            }
            Err(e) => {
                error!("Failed to install SIGTERM handler: {}", e);
            }
        }
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {},
    }

    info!("Shutdown signal received");
}
