#![warn(unused_extern_crates)]
#![allow(non_snake_case)]

use std::{env, thread};

use diesel::{
    SqliteConnection,
    r2d2::{self, ConnectionManager},
};
use dotenvy::dotenv;
use ntex::web;
use ntex_files as fs;

mod api;
mod db;
mod models;
mod providers;
mod proxies;
mod proxy;
mod schema;
mod status;
mod uploaders;
mod util;
mod videos;

type DbPool = r2d2::Pool<ConnectionManager<SqliteConnection>>;

// #[macro_use(c)]
// extern crate cute;

#[ntex::main]
async fn main() -> std::io::Result<()> {
    // std::env::set_var("RUST_BACKTRACE", "1");
    dotenv().ok();

    // Enable request logging
    if std::env::var("RUST_LOG").is_err() {
        unsafe {
            std::env::set_var("RUST_LOG", "warn");
        }
    }
    env_logger::init(); // You need this to actually see logs
    crate::flow_debug!(
        "startup begin rust_log={} debug_compiled={}",
        std::env::var("RUST_LOG").unwrap_or_else(|_| "unset".to_string()),
        cfg!(feature = "debug")
    );

    // set up database connection pool
    let connspec = std::env::var("DATABASE_URL").expect("DATABASE_URL");
    let manager = ConnectionManager::<SqliteConnection>::new(connspec.clone());
    let pool = r2d2::Pool::builder()
        .build(manager)
        .expect("Failed to create pool.");
    crate::flow_debug!(
        "database pool ready database_url={}",
        crate::util::flow_debug::preview(&connspec, 96)
    );

    let mut requester = util::requester::Requester::new();
    let proxy_enabled = env::var("PROXY").unwrap_or("0".to_string()) != "0".to_string();
    requester.set_proxy(proxy_enabled);
    crate::flow_debug!("requester initialized proxy_enabled={}", proxy_enabled);

    let cache: util::cache::VideoCache = crate::util::cache::VideoCache::new()
        .max_size(100_000)
        .to_owned();
    crate::flow_debug!("video cache initialized max_size=100000");
    cache.spawn_hourly_cleanup();
    crate::flow_debug!("video cache hourly cleanup task spawned");
    let _ = providers::configure_runtime_validation(pool.clone(), cache.clone(), requester.clone());

    thread::spawn(move || {
        crate::flow_debug!("provider init thread spawned");
        // Create a tiny runtime just for these async tasks
        let rt = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .expect("build tokio runtime");

        rt.block_on(async move {
            crate::flow_debug!("provider init begin");
            providers::init_providers_now();
            crate::flow_debug!("provider init complete");
        });
    });

    crate::flow_debug!("http server binding addr=0.0.0.0:18080 workers=8");
    web::HttpServer::new(move || {
        let pool = pool.clone();
        let cache = cache.clone();
        let requester = requester.clone();
        async move {
            web::App::new()
                .state(pool)
                .state(cache)
                .state(requester)
                .middleware(web::middleware::Logger::default())
                .service(web::scope("/api").configure(api::config))
                .service(web::scope("/proxy").configure(proxy::config))
                .service(
                    web::resource("/").route(web::get().to(|req: web::HttpRequest| async move {
                        let host = match std::env::var("DOMAIN") {
                            Ok(d) => d,
                            Err(_) => req.connection_info().host().to_string(),
                        };
                        let source_forward_header = format!("hottub://source?url={}", host);
                        web::HttpResponse::Found()
                            .header("Location", source_forward_header)
                            .finish()
                    })),
                )
                .service(fs::Files::new("/", "static").index_file("index.html"))
        }
    })
    // .bind_openssl(("0.0.0.0", 18080), builder)?
    .bind(("0.0.0.0", 18080))?
    .run()
    .await
}
