mod api;
mod config;
mod error;
mod formatter;
mod mapping;
mod message_consumer;
// mod persistence; // Removed in favor of common_contracts::persistence_client
mod state;
mod ts_client;
mod tushare;
mod worker;
mod generic_worker;
mod workflow_adapter;
mod config_poller;

use crate::config::AppConfig;
use crate::error::{Result, AppError};
use crate::state::AppState;
use tracing::{info, warn};
use common_contracts::lifecycle::ServiceRegistrar;
use common_contracts::registry::{ServiceRegistration, ProviderMetadata, ConfigFieldSchema, FieldType, ConfigKey};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<()> {
    // Initialize logging
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    info!("Starting tushare-provider-service...");

    // Load configuration
    let config = AppConfig::load().map_err(|e| AppError::Configuration(e.to_string()))?;
    let port = config.server_port;

    // Initialize application state
    let app_state = AppState::new(config.clone());

    // --- Start the config poller ---
    tokio::spawn(config_poller::run_config_poller(app_state.clone()));

    // Create the Axum router
    let app = api::create_router(app_state.clone());

    // --- Start the message consumer ---
    tokio::spawn(message_consumer::run(app_state));

    // --- Service Registration ---
    let registrar = ServiceRegistrar::new(
        config.api_gateway_url.clone(),
        ServiceRegistration {
            service_id: format!("{}-{}", "tushare-provider", uuid::Uuid::new_v4()),
            service_name: "tushare".to_string(),
            role: common_contracts::registry::ServiceRole::DataProvider,
            base_url: format!("http://{}:{}", config.service_host, port),
            health_check_url: format!("http://{}:{}/health", config.service_host, port),
            metadata: Some(ProviderMetadata {
                id: "tushare".to_string(),
                name_en: "Tushare Pro".to_string(),
                name_cn: "Tushare Pro (中国股市)".to_string(),
                description: "Official Tushare Data Provider".to_string(),
                icon_url: None,
                config_schema: vec![
                    ConfigFieldSchema {
                        key: ConfigKey::ApiToken,
                        label: "API Token".to_string(),
                        field_type: FieldType::Password,
                        required: true,
                        placeholder: Some("Enter your token...".to_string()),
                        default_value: None,
                        description: Some("Get it from https://tushare.pro".to_string()),
                        options: None,
                    },
                ],
                supports_test_connection: true,
            }),
        }
    );

    // Initial registration attempt (best effort)
    let _ = registrar.register().await;

    // Start heartbeat loop
    let registrar = Arc::new(registrar);
    tokio::spawn(registrar.clone().start_heartbeat_loop());

    // Start the HTTP server with graceful shutdown
    let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port))
        .await
        .unwrap();
    info!("HTTP server listening on port {}", port);

    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal(registrar))
        .await
        .unwrap();

    Ok(())
}

async fn shutdown_signal(registrar: Arc<ServiceRegistrar>) {
    let ctrl_c = async {
        tokio::signal::ctrl_c()
            .await
            .expect("failed to install Ctrl+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
            .expect("failed to install signal handler")
            .recv()
            .await;
    };

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

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

    info!("Shutdown signal received, deregistering service...");
    if let Err(e) = registrar.deregister().await {
        warn!("Failed to deregister service: {}", e);
    } else {
        info!("Service deregistered successfully.");
    }
}
