use crate::common::{get_arg, get_arg_or};
use crate::database::Database;
use std::path::PathBuf;
use std::sync::Arc;

#[derive(Clone)]
pub struct ApiConfig {
    pub login_options: Vec<String>,
    pub sysinfo_ver: String,
    pub session_ttl_secs: i64,
    /// When true, `/api/ab/personal` returns 404, forcing the client into the
    /// legacy single-blob AB path (`GET/POST /api/ab`). The default is the
    /// modern shared-AB path.
    pub ab_legacy_mode: bool,
    /// Surfaced verbatim via `/api/ab/settings.max_peer_one_ab`.
    pub ab_max_peers_per_book: i64,
    /// On-disk root for `/api/record` uploads. Created on first use; one
    /// subdirectory per peer-id under here.
    pub recording_dir: PathBuf,
    /// 0 means unlimited.
    pub recording_max_size_bytes: u64,
    /// 0 means no retention sweep.
    pub audit_retention_days: i64,
    /// SMTP transport for email-code login. `None` = dev mode: codes are
    /// logged to stdout instead of mailed.
    pub email: Option<EmailConfig>,
    /// Externally reachable base URL of this server, e.g. for the OIDC
    /// redirect_uri. Empty disables OIDC.
    pub public_base_url: String,
    /// On-disk root for the admin dashboard's static files. Empty disables
    /// the dashboard entirely.
    pub admin_ui_dir: String,
}

/// SMTP wiring for email-code login.
#[derive(Clone, Debug)]
pub struct EmailConfig {
    pub host: String,
    pub port: u16,
    pub username: Option<String>,
    pub password: Option<String>,
    pub from: String,
    pub starttls: bool,
}

#[derive(Clone)]
pub struct AppState {
    pub db: Database,
    pub cfg: ApiConfig,
}

impl AppState {
    pub fn new(db: Database) -> Arc<Self> {
        let ab_legacy_mode = matches!(
            get_arg_or("ab-legacy-mode", "off".to_string())
                .to_ascii_lowercase()
                .as_str(),
            "on" | "y" | "yes" | "true" | "1"
        );
        let ab_max_peers_per_book: i64 = get_arg_or("ab-max-peers-per-book", "100".to_string())
            .parse()
            .unwrap_or(100);
        let recording_dir =
            PathBuf::from(get_arg_or("recording-dir", "./recordings".to_string()));
        let recording_max_size_bytes: u64 = get_arg_or("recording-max-size-mb", "0".to_string())
            .parse::<u64>()
            .unwrap_or(0)
            .saturating_mul(1024 * 1024);
        let audit_retention_days: i64 = get_arg_or("audit-retention-days", "0".to_string())
            .parse()
            .unwrap_or(0);
        let email = build_email_config();
        let public_base_url = get_arg("public-base-url");
        let admin_ui_dir = get_arg_or("admin-ui-dir", "./admin_ui".to_string());
        // login_options advertises every login method this server accepts.
        // The Flutter client uses this to render the matching button on the
        // sign-in dialog. `email_code` and `oidc/<name>` are opt-in so a
        // deployment without SMTP / OIDC doesn't dangle a broken button.
        let mut login_options = vec!["account".to_string()];
        if email.is_some() || std::env::var("ALLOW_DEV_EMAIL_CODE").is_ok() {
            login_options.push("email_code".to_string());
        }
        // OIDC providers are mounted dynamically — actual provider names are
        // appended later by the oidc::providers loader once the DB rows exist.
        Arc::new(Self {
            db,
            cfg: ApiConfig {
                login_options,
                sysinfo_ver: "m1-1".to_string(),
                session_ttl_secs: 30 * 86400,
                ab_legacy_mode,
                ab_max_peers_per_book,
                recording_dir,
                recording_max_size_bytes,
                audit_retention_days,
                email,
                public_base_url,
                admin_ui_dir,
            },
        })
    }
}

fn build_email_config() -> Option<EmailConfig> {
    let host = get_arg("smtp-host");
    if host.is_empty() {
        return None;
    }
    let port: u16 = get_arg_or("smtp-port", "587".to_string())
        .parse()
        .unwrap_or(587);
    let username = {
        let u = get_arg("smtp-user");
        if u.is_empty() { None } else { Some(u) }
    };
    let password = {
        let p = get_arg("smtp-pass");
        if p.is_empty() { None } else { Some(p) }
    };
    let from = {
        let f = get_arg("smtp-from");
        if f.is_empty() {
            format!("noreply@{}", host)
        } else {
            f
        }
    };
    let starttls = matches!(
        get_arg_or("smtp-tls", "on".to_string()).to_ascii_lowercase().as_str(),
        "on" | "y" | "yes" | "true" | "1"
    );
    Some(EmailConfig {
        host,
        port,
        username,
        password,
        from,
        starttls,
    })
}
