//! 充电柜矩阵系统 — API 服务入口
//!
//! 提供 HTTP API（后台管理 + H5），处理后端业务逻辑。
//! TCP 设备通信由独立的 device-server 处理，通过 Redis 队列通信。

mod commands;
mod config;
mod db;
mod error;
mod middleware;
mod nodes;
mod protocol;
mod redis;
mod routes;
mod workers;

use std::net::SocketAddr;
use tracing_subscriber::EnvFilter;

use commands::{AppState, NodeRegistry, PendingCommands};

#[tokio::main]
async fn main() {
    // 初始化日志
    tracing_subscriber::fmt()
        .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
        .init();

    // 加载配置
    let cfg = config::Config::from_env();
    cfg.validate();
    tracing::info!("配置加载完成");

    // 初始化 MySQL 连接池
    let db_pool = db::init_pool(&cfg.database_url)
        .await
        .expect("MySQL 连接失败");
    tracing::info!("数据库连接成功");

    // 执行自动迁移
    db::migrate::run(&db_pool)
        .await
        .expect("数据库迁移失败");
    tracing::info!("数据库迁移完成");

    // 初始化 Redis 连接
    let redis_conn = redis::init_connection(&cfg.redis_url)
        .await
        .expect("Redis 连接失败");
    tracing::info!("Redis 连接成功");

    // 初始化共享状态
    let pending_commands = PendingCommands::new();
    let nodes = NodeRegistry::new(redis_conn.clone());
    let state = AppState {
        redis: redis_conn.clone(),
        mysql: db_pool.clone(),
        pending_commands: pending_commands.clone(),
        nodes: nodes.clone(),
    };

    // 启动后台协程 — 处理设备上报
    let report_redis = redis_conn.clone();
    let report_mysql = db_pool.clone();
    tokio::spawn(async move {
        workers::report_worker::start(report_redis, report_mysql).await;
    });

    // 启动后台协程 — 处理指令响应
    let reply_redis = redis_conn.clone();
    tokio::spawn(async move {
        workers::reply_worker::start(reply_redis, pending_commands).await;
    });

    // 启动后台协程 — 节点心跳超时检测
    let checker_nodes = nodes.clone();
    let heartbeat_timeout = cfg.node_heartbeat_timeout;
    tokio::spawn(async move {
        workers::node_checker::run(checker_nodes, heartbeat_timeout).await;
    });

    // 构建 HTTP 路由
    let app = routes::build(state);

    // 启动 HTTP 服务
    let addr: SocketAddr = format!("{}:{}", cfg.host, cfg.port)
        .parse()
        .expect("无效的监听地址");
    tracing::info!("HTTP 服务器启动于 http://{}", addr);

    let listener = tokio::net::TcpListener::bind(addr)
        .await
        .expect("端口绑定失败");
    axum::serve(listener, app)
        .await
        .expect("服务器运行失败");
}
