use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};

use rpassword::read_password;

use crate::AppResult;

const DEFAULT_BASE_URL: &str = "https://tgn.ashrhmn.com";

#[derive(Clone)]
pub(crate) struct Config {
    pub(crate) base_url: String,
    pub(crate) token: String,
    pub(crate) debug: bool,
}

pub(crate) fn base_url() -> String {
    std::env::var("TGN_BASE_URL")
        .unwrap_or_else(|_| DEFAULT_BASE_URL.to_string())
        .trim_end_matches('/')
        .to_string()
}

fn config_path() -> AppResult<PathBuf> {
    if let Some(xdg_config_home) = std::env::var_os("XDG_CONFIG_HOME") {
        return Ok(PathBuf::from(xdg_config_home).join("tgn").join("config"));
    }

    Ok(dirs::home_dir()
        .ok_or_else(|| io::Error::other("Unable to determine home directory"))?
        .join(".config")
        .join("tgn")
        .join("config"))
}

fn get_token_from_config() -> Option<String> {
    let contents = fs::read_to_string(config_path().ok()?).ok()?;
    contents.lines().find_map(|line| {
        line.strip_prefix("TGN_TOKEN=")
            .map(str::trim)
            .filter(|token| !token.is_empty())
            .map(str::to_string)
    })
}

pub(crate) fn get_token() -> AppResult<String> {
    std::env::var("TGN_TOKEN")
        .ok()
        .filter(|token| !token.trim().is_empty())
        .or_else(get_token_from_config)
        .ok_or_else(|| {
            io::Error::other("TGN_TOKEN not found in environment variables or config file.").into()
        })
}

pub(crate) fn handle_login() -> AppResult<()> {
    println!("TGN Login");
    println!("---------");
    println!();
    println!("To get a token:");
    println!("  1. Message @tgnnnn_bot on Telegram");
    println!("  2. Send /start to register");
    println!("  3. Send /add <device_name> to create a device");
    println!();
    print!("Enter your TGN token: ");
    io::stdout().flush()?;

    let token = read_password()?;
    let token = token.trim();

    if token.is_empty() {
        return Err(io::Error::other("Token cannot be empty").into());
    }

    if !token.starts_with("tgn_") {
        return Err(io::Error::other("Invalid token").into());
    }

    let path = config_path()?;
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
        set_permissions(parent, 0o700)?;
    }

    fs::write(&path, format!("TGN_TOKEN={token}\n"))?;
    set_permissions(&path, 0o600)?;

    println!();
    println!("Token saved to {}", path.display());
    println!("You can now send notifications with: tgn \"Your message\"");
    Ok(())
}

#[cfg(unix)]
fn set_permissions(path: &Path, mode: u32) -> AppResult<()> {
    use std::os::unix::fs::PermissionsExt;
    let permissions = fs::Permissions::from_mode(mode);
    fs::set_permissions(path, permissions)?;
    Ok(())
}

#[cfg(not(unix))]
fn set_permissions(_path: &Path, _mode: u32) -> AppResult<()> {
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_base_url_has_no_trailing_slash() {
        assert!(!DEFAULT_BASE_URL.ends_with('/'));
    }
}
