//! `fxt auth` -- authentication and session management.

use anyhow::Result;
use clap::{Parser, Subcommand};
use colored::Colorize;
use serde::Serialize;

use crate::auth::token_manager::{AuthTokenManager, FileTokenStorage};
use crate::auth::LoginClient;
use crate::grpc::FoxhuntClient;
use crate::output::{self, HumanReadable, OutputFormat};

#[derive(Parser, Debug)]
pub struct AuthCommand {
    #[command(subcommand)]
    action: AuthAction,
}

#[derive(Subcommand, Debug)]
enum AuthAction {
    /// Login with username and password
    Login {
        /// Username
        #[arg(long)]
        username: Option<String>,
    },
    /// Logout and clear stored tokens
    Logout,
    /// Show current authentication status
    Status,
}

// ── Result types ──────────────────────────────────────────────────────

#[derive(Serialize)]
struct LoginResult {
    success: bool,
    message: String,
}

impl HumanReadable for LoginResult {
    fn print_human(&self) {
        if self.success {
            println!("{} {}", "Authenticated.".green().bold(), self.message);
        } else {
            println!("{} {}", "Login failed.".red().bold(), self.message);
        }
    }
}

#[derive(Serialize)]
struct LogoutResult {
    success: bool,
    message: String,
}

impl HumanReadable for LogoutResult {
    fn print_human(&self) {
        if self.success {
            println!("{} {}", "Logged out.".green().bold(), self.message);
        } else {
            println!("{} {}", "Logout failed.".red().bold(), self.message);
        }
    }
}

#[derive(Serialize)]
struct AuthStatusResult {
    authenticated: bool,
    has_refresh_token: bool,
    expires_in: Option<String>,
}

impl HumanReadable for AuthStatusResult {
    fn print_human(&self) {
        println!("{}", "Authentication Status".cyan().bold());
        if self.authenticated {
            println!("  Status: {}", "authenticated".green());
            if let Some(ref exp) = self.expires_in {
                println!("  Token expires in: {exp}");
            }
        } else {
            println!("  Status: {}", "not authenticated".red());
            if self.has_refresh_token {
                println!(
                    "  {}",
                    "Refresh token found -- run `fxt auth login` to re-authenticate.".yellow()
                );
            } else {
                println!("  Run `fxt auth login` to authenticate.");
            }
        }
    }
}

// ── Execute ───────────────────────────────────────────────────────────

impl AuthCommand {
    pub async fn execute(&self, client: &FoxhuntClient, format: OutputFormat) -> Result<()> {
        match &self.action {
            AuthAction::Login { username } => {
                let storage = FileTokenStorage::new()?;
                let manager = AuthTokenManager::new(storage);
                let login_client = LoginClient::new(client.channel());

                if let Some(user) = username {
                    // Password: FXT_PASSWORD env var > interactive prompt
                    // (never accept --password flag — visible in ps/history)
                    let pw = if let Ok(env_pw) = std::env::var("FXT_PASSWORD") {
                        env_pw
                    } else {
                        print!("Password: ");
                        std::io::Write::flush(&mut std::io::stdout())?;
                        rpassword::read_password().map_err(|e| anyhow::anyhow!("{e}"))?
                    };

                    login_client
                        .login_with_credentials(user, &pw, &manager)
                        .await?;

                    let result = LoginResult {
                        success: true,
                        message: format!("Logged in as {user}"),
                    };
                    output::render(&result, format)?;
                } else {
                    // Interactive login flow
                    login_client.interactive_login(&manager).await?;

                    let result = LoginResult {
                        success: true,
                        message: "Interactive login complete.".to_owned(),
                    };
                    output::render(&result, format)?;
                }
            }

            AuthAction::Logout => {
                let storage = FileTokenStorage::new()?;
                let manager = AuthTokenManager::new(storage);
                manager.clear_tokens().await?;

                let result = LogoutResult {
                    success: true,
                    message: "All tokens cleared.".to_owned(),
                };
                output::render(&result, format)?;
            }

            AuthAction::Status => {
                let storage = FileTokenStorage::new()?;
                let manager = AuthTokenManager::new(storage);

                let has_token = manager.has_valid_token().await;
                let has_refresh = manager.get_refresh_token().await?.is_some();
                #[allow(clippy::integer_division)]
                let expires_in = manager.time_until_expiry().await.map(|d| {
                    let secs = d.as_secs();
                    if secs >= 3600 {
                        format!("{}h {}m", secs / 3600, (secs % 3600) / 60)
                    } else if secs >= 60 {
                        format!("{}m {}s", secs / 60, secs % 60)
                    } else {
                        format!("{secs}s")
                    }
                });

                let result = AuthStatusResult {
                    authenticated: has_token,
                    has_refresh_token: has_refresh,
                    expires_in,
                };
                output::render(&result, format)?;
            }
        }
        Ok(())
    }
}
