use actix_web::middleware::from_fn;
use actix_web::{App, HttpServer};
use common::{CERT_FILE_PATH, GAME_SERVER_ADDRESS, KEY_FILE_PATH, init_tracing};
use database::{DatabaseSettings, connect_to, run_migrations};
use middleware::{auth::auth_middleware, logger::Logger};
use tracing::{error, info};

use actix_web::web::Data;
use rustls::{
    ServerConfig,
    pki_types::{CertificateDer, PrivateKeyDer},
};
use rustls_pemfile::{certs, pkcs8_private_keys, rsa_private_keys};
use std::{fs::File, io::BufReader};

mod middleware;
mod routes;

#[actix_web::main]
async fn main() -> anyhow::Result<()> {
    {
        init_tracing();

        let data_path = get_data_path()?;
        info!("Found data path: {}", data_path.display());

        let settings = DatabaseSettings::default();

        let db = connect_to(&settings).await.map_err(|e| {
            eprintln!("Database connection error: {:?}", e);
            std::io::Error::new(std::io::ErrorKind::Other, e)
        })?;

        run_migrations(&db).await.map_err(|e| {
            eprintln!("Migration error: {:?}", e);
            std::io::Error::new(std::io::ErrorKind::Other, e)
        })?;

        info!("Loading game data...");
        data::exceldb::init(data_path.to_str().unwrap()).map_err(|e| {
            error!("Failed to load game data: {:#}", e);
            e
        })?;
        info!("Game data loaded");

        let cert_file = &mut BufReader::new(File::open(&*CERT_FILE_PATH)?);
        let cert_chain: Vec<CertificateDer> = certs(cert_file).collect::<Result<Vec<_>, _>>()?;

        let mut keys: Vec<PrivateKeyDer> = {
            let mut key_reader = BufReader::new(File::open(&*KEY_FILE_PATH)?);
            let pkcs8 = pkcs8_private_keys(&mut key_reader)
                .collect::<Result<Vec<_>, _>>()?
                .into_iter()
                .map(PrivateKeyDer::Pkcs8)
                .collect::<Vec<_>>();

            if !pkcs8.is_empty() {
                pkcs8
            } else {
                let mut key_reader = BufReader::new(File::open(&*KEY_FILE_PATH)?);
                rsa_private_keys(&mut key_reader)
                    .collect::<Result<Vec<_>, _>>()?
                    .into_iter()
                    .map(PrivateKeyDer::Pkcs1)
                    .collect()
            }
        };

        let mut tls_config = ServerConfig::builder()
            .with_no_client_auth()
            .with_single_cert(cert_chain, keys.remove(0))?;
        tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];

        let app_factory = move || {
            App::new()
                .wrap(from_fn(Logger))
                .wrap(from_fn(auth_middleware))
                .app_data(Data::new(db.clone()))
                // Basic routes
                .service(routes::basic::notice_info::notice_info_handler)
                .service(routes::basic::maintenace_info::maintenance_info_handler)
                .service(routes::basic::server_info::server_info_handler)
                .service(routes::basic::state_check_info_json::state_check_info_handle)
                // User routes
                .service(routes::user::login_user::login_user_handler)
                // Default routes
                .service(routes::default::balance_version_check::balance_check_version_handler)
                // Batch route (main batch endpoint)
                .service(routes::default::batch::batch::batch_request_handler)
                .service(routes::default::batch::batch::mission_info_handler)
                .service(routes::default::batch::batch::char_info_handler)
                .service(routes::default::batch::batch::recipe_info_handler)
                .service(routes::game::login_event::login_event_handler)
                .service(routes::game::schedule_info::schedule_info_handler)
                .service(routes::game::event_mission_info::event_mission_info_handler)
                .service(routes::game::event_reward_history::event_reward_history_handler)
                .service(routes::game::pack_event_story_info::pack_event_story_info_handler)
                .service(routes::game::pack_event_battle_info::pack_event_battle_info_handler)
                .service(routes::game::pictorial_book_info::pictorial_book_info_handler)
                .service(routes::game::attendance::attendance_info_handler)
                .service(routes::game::all_char_refresh::all_char_refresh_info_handler)
                .service(routes::game::save_total_battle_power::save_total_battle_power_handler)
                .service(routes::game::charge_cost_info::charge_cost_info_handler)
                .service(routes::basic::localization::localization_handler)
                .service(routes::basic::loki::loki_handler)
                .service(routes::game::equip_info::equip_info_handler)
                .service(routes::game::pack_preview_info::pack_preview_info_handler)
                .service(routes::game::monster_info::monster_info_handler)
                .service(routes::game::today_quest_info::today_quest_info_handler)
                .service(routes::game::waypoint_info::waypoint_info_handler)
                .service(routes::game::field_deck_info::field_deck_info_handler)
                .service(routes::game::hunting_ground_info_list::hunting_ground_info_list_handler)
                .service(routes::game::hunting_ground_info::hunting_ground_info_handler)
                .service(routes::game::tutorial_clear::tutorial_clear_handler)
                .service(routes::game::notice_detail_info::notice_detail_info_handler)
                .service(routes::game::save_user_position::save_user_position_handler)
                .service(routes::game::waypoint_save::waypoint_save_handler)
                .service(routes::game::server_now_time::server_now_time_handler)
                .service(routes::game::field_object_reward::field_object_reward_handler)
        };

        info!("Starting server on {}", &GAME_SERVER_ADDRESS);

        HttpServer::new(app_factory)
            .bind((GAME_SERVER_ADDRESS, 8082))? // HTTP
            .bind_rustls_0_22((GAME_SERVER_ADDRESS, 8443), tls_config)? // HTTPS (Rustls 0.23)
            .run()
            .await?;

        Ok(())
    }
}

use std::path::PathBuf;

fn get_data_path() -> anyhow::Result<PathBuf> {
    // Always use CARGO_MANIFEST_DIR in development
    let base_dir = if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
        PathBuf::from(manifest_dir)
    } else {
        // Fallback: try to find project root by looking for Cargo.toml
        let mut current = std::env::current_dir()?;
        loop {
            if current.join("Cargo.toml").exists() {
                break;
            }
            if !current.pop() {
                return Err(anyhow::anyhow!("Could not find project root"));
            }
        }
        current
    };

    let data_path = base_dir.join("data\\tables");

    if !data_path.exists() {
        return Err(anyhow::anyhow!(
            "Data directory not found at: {}\nExpected to find it at project root",
            data_path.display()
        ));
    }

    Ok(data_path)
}
