#![deny(clippy::unwrap_used)]
#![warn(clippy::all, clippy::nursery, clippy::cargo, clippy::pedantic)]

#[macro_use]
extern crate rocket;

pub mod api;
pub mod cli;
pub mod error;
pub mod messenger;
pub mod model;
pub mod repo;
pub mod svc;

use crate::repo::channel_repo::ChannelRepository;
use crate::repo::message_repo::MessageRepository;
use crate::repo::space_repo::SpaceRepository;
use crate::repo::user_repo::UserRepository;
use crate::repo::{Repo, access_token_repo::AccessTokenRepo};
use crate::svc::access_token_svc::AccessTokenService;
use crate::svc::auth_svc::AuthService;
use crate::svc::chat_svc::ChatService;
use crate::svc::llm_service::LlmService;
use crate::svc::settings_svc::SettingsService;
use crate::svc::user_svc::UserService;
use api::cdn;
use rocket::http::Method;
use rocket_cors::{AllowedOrigins, CorsOptions};
use sqlx::postgres::PgPoolOptions;
use std::sync::Arc;
use std::time::Duration;

pub fn rocket() -> rocket::Rocket<rocket::Build> {

    if let Ok(var) = std::env::var("RELEASE_MODE") && var == "1" {

    } else {
        dotenv::dotenv().expect("Failed to load .env file");
    }


    let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");

    println!("Running with database URL: {}", db_url);

    let pool = PgPoolOptions::new()
        .max_connections(25)
        .min_connections(5)
        .acquire_timeout(Duration::from_secs(5))
        .connect_lazy(&db_url)
        .expect("Failed to create database pool");

    let user_repo = Arc::new(UserRepository::new(pool.clone()));
    let message_repo = MessageRepository::new(pool.clone());
    let token_repo = Arc::new(AccessTokenRepo::new(pool.clone()));
    let space_repo: Arc<dyn repo::SpaceRepo> = Arc::new(SpaceRepository::new(pool.clone()));
    let channel_repo: Arc<dyn repo::ChannelRepo> = Arc::new(ChannelRepository::new(pool.clone()));
    let llm_service = LlmService::new();
    let chat_service = ChatService::new(
        32,
        llm_service.clone(),
        message_repo.clone(),
        user_repo.clone(),
        channel_repo.clone(),
        space_repo.clone(),
    );

    rocket_builder(
        user_repo,
        token_repo,
        space_repo,
        channel_repo,
        chat_service,
    )
}

pub fn rocket_builder(
    user_repo: Arc<dyn repo::UserRepo>,
    token_repo: Arc<dyn repo::AccessTokenRepoTrait>,
    space_repo: Arc<dyn repo::SpaceRepo>,
    channel_repo: Arc<dyn repo::ChannelRepo>,
    chat_service: ChatService,
) -> rocket::Rocket<rocket::Build> {
    let cors = CorsOptions::default()
        .allowed_origins(AllowedOrigins::all())
        .allowed_methods(
            vec![Method::Get, Method::Post, Method::Patch]
                .into_iter()
                .map(From::from)
                .collect(),
        )
        .allow_credentials(true)
        .to_cors()
        .expect("unable to create cors");

    let access_token_svc = AccessTokenService::new(token_repo.clone());
    let auth_service = AuthService::new(user_repo.clone(), access_token_svc.clone());
    let settings_service = SettingsService::new(auth_service.clone(), user_repo.clone());
    let user_service = UserService::new(user_repo.clone());

    rocket::build()
        .manage(chat_service)
        .manage(auth_service)
        .manage(settings_service)
        .manage(access_token_svc)
        .manage(user_service)
        .manage(space_repo)
        .manage(channel_repo)
        .attach(cors)
        .mount(
            "/api",
            routes![
                cdn::upload_profile_pic,
                api::profile::display_name,
                // basic auth
                api::auth::login,
                api::auth::signup,
                api::auth::generate_invite,
                // 2fa
                api::totp::confirm_totp,
                api::totp::disable_totp,
                api::totp::get_totp,
                api::totp::get_totp_status,
                api::totp::verify_totp,
                // chat
                api::chat::event_stream,
                api::chat::post_message,
                // user settings
                api::settings::change_display_name,
                api::settings::change_password,
                api::settings::change_username,
                api::settings::delete_account,
                // spaces
                api::space::list_spaces,
                api::space::list_channels,
                api::space::get_accessible_channels
            ],
        )
        // .register(
        //     "/",
        //     catchers![error::handle_401, error::handle_404, error::handle_default,],
        // )
}
