use std::env;
use std::time::Duration;

use actix_identity::IdentityMiddleware;
use actix_session::SessionMiddleware;
use actix_web::body::MessageBody;
use actix_web::cookie::Key;
use actix_web::dev::{ServiceFactory, ServiceRequest, ServiceResponse};
use actix_web::{web, App, HttpServer};
use actix_web_static_files::ResourceFiles;
use brass_config::{get_env, load_config, Config};
use lettre::Transport;
use sqlx::postgres::PgPool;
use sqlx::{Pool, Postgres};

use crate::postgres_session_store::SqlxPostgresqlSessionStore;
use crate::utils::manage_commands::{handle_command, parse_args};

mod auth;
mod endpoints;
mod middleware;
mod models;
mod utils;

mod filters;
mod postgres_session_store;

include!(concat!(env!("OUT_DIR"), "/generated.rs"));
include!(concat!(env!("OUT_DIR"), "/built.rs"));

#[actix_web::main]
async fn main() -> anyhow::Result<()> {
    let env = get_env()?;
    let config: Config = load_config(&env)?;

    let args = parse_args()?;

    let pool = PgPool::connect(&config.database_url).await?;
    let mailer = utils::email::get_mailer()?;

    handle_command(args.command, &pool).await?;

    let address = config.server_address;
    let port = config.server_port;
    println!("Starting server on http://{address}:{port}.",);

    HttpServer::new(move || create_app(config.clone(), pool.clone(), mailer.clone()))
        .bind((address, port))?
        .run()
        .await?;

    Ok(())
}

pub fn create_app<T>(
    config: Config,
    pool: Pool<Postgres>,
    mailer: T,
) -> App<
    impl ServiceFactory<
        ServiceRequest,
        Response = ServiceResponse<impl MessageBody>,
        Config = (),
        InitError = (),
        Error = actix_web::error::Error,
    >,
> where T: Transport + 'static {
    let generated = generate();
    let secret_key = Key::from(config.secret_key.as_bytes());
    let store = SqlxPostgresqlSessionStore::from_pool(pool.clone().into());

    App::new()
        .app_data(web::Data::new(pool))
        .app_data(web::Data::new(mailer))
        .configure(endpoints::init)
        .wrap(middleware::RedirectToLogin)
        .wrap(middleware::LoadCurrentUser)
        .wrap(
            IdentityMiddleware::builder()
                .visit_deadline(Some(Duration::from_secs(300)))
                .build(),
        )
        .wrap(SessionMiddleware::new(store.clone(), secret_key.clone()))
        .service(ResourceFiles::new("/static", generated))
}
