//! OSTP Client CLI - Stealth VPN Client for Windows
//!
//! Usage:
//!   ostp-client connect --server 1.2.3.4:8443 --psk <hex-key>
//!   ostp-client --config %APPDATA%/ostp/client.json

use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use console::{style, Emoji};
use dialoguer::{Confirm, Input, Select};
use ostp::{ClientConfig, OstpClient};
use ostp_guard::error_codes;
use std::net::SocketAddr;
use std::path::PathBuf;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};

static ROCKET: Emoji<'_, '_> = Emoji("🚀 ", "");
static LOCK: Emoji<'_, '_> = Emoji("🔒 ", "");
static CHECK: Emoji<'_, '_> = Emoji("✅ ", "[OK] ");
static WARN: Emoji<'_, '_> = Emoji("⚠️  ", "[!] ");
static GLOBE: Emoji<'_, '_> = Emoji("🌍 ", "");

#[derive(Parser)]
#[command(name = "ostp-client")]
#[command(author = "ospab.team")]
#[command(version)]
#[command(about = "OSTP Stealth VPN Client", long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,

    /// Path to config file (JSON)
    #[arg(short, long)]
    config: Option<PathBuf>,

    /// Log level (trace, debug, info, warn, error)
    #[arg(long, default_value = "info")]
    log_level: String,
}

#[derive(Subcommand)]
enum Commands {
    /// Connect to an OSTP server
    Connect {
        /// Server address (e.g., vpn.example.com:8443)
        #[arg(short, long)]
        server: SocketAddr,

        /// Pre-shared key in hex format
        #[arg(short, long, env = "OSTP_PSK")]
        psk: String,

        /// Country code for SNI mimicry (RU, US, DE, NO, CN)
        #[arg(short = 'c', long, default_value = "US")]
        country: String,
    },
    /// Interactive setup wizard
    Setup,
    /// Test connection to server
    Test {
        /// Server address
        #[arg(short, long)]
        server: SocketAddr,

        /// Pre-shared key in hex format
        #[arg(short, long)]
        psk: String,
    },
    /// Show saved profiles
    Profiles,
}

#[derive(serde::Deserialize, serde::Serialize, Clone)]
#[allow(dead_code)]
struct ClientConfigFile {
    server: String,
    psk: String,
    country: Option<String>,
    log_level: Option<String>,
}

#[derive(serde::Deserialize, serde::Serialize, Default)]
struct ProfilesFile {
    profiles: Vec<Profile>,
    default_profile: Option<String>,
}

#[derive(serde::Deserialize, serde::Serialize, Clone)]
struct Profile {
    name: String,
    server: String,
    psk: String,
    country: String,
}

fn setup_logging(level: &str) {
    let filter = EnvFilter::try_from_default_env()
        .unwrap_or_else(|_| EnvFilter::new(level));

    tracing_subscriber::registry()
        .with(fmt::layer()
            .with_target(false)
            .with_thread_ids(false)
            .without_time())
        .with(filter)
        .init();
}

fn parse_psk(hex_str: &str) -> Result<[u8; 32]> {
    let bytes = hex::decode(hex_str.trim())
        .context("Invalid hex string for PSK")?;
    
    if bytes.len() != 32 {
        anyhow::bail!("PSK must be exactly 32 bytes (64 hex characters), got {}", bytes.len());
    }
    
    let mut psk = [0u8; 32];
    psk.copy_from_slice(&bytes);
    Ok(psk)
}

fn get_config_dir() -> PathBuf {
    #[cfg(windows)]
    {
        std::env::var("APPDATA")
            .map(PathBuf::from)
            .unwrap_or_else(|_| PathBuf::from("."))
            .join("ostp")
    }
    #[cfg(not(windows))]
    {
        std::env::var("HOME")
            .map(PathBuf::from)
            .unwrap_or_else(|_| PathBuf::from("."))
            .join(".config")
            .join("ostp")
    }
}

fn print_banner() {
    println!();
    println!("{}", style("╔════════════════════════════════════════════════════════╗").cyan());
    println!("{}", style("║           OSTP Stealth VPN Client                      ║").cyan());
    println!("{}", style("║                                                        ║").cyan());
    println!("{}", style("║   Invisible by Default • Contextual Mimicry            ║").cyan());
    println!("{}", style("╚════════════════════════════════════════════════════════╝").cyan());
    println!();
}

async fn interactive_setup() -> Result<()> {
    print_banner();
    
    println!("{}Welcome to OSTP Client Setup Wizard\n", ROCKET);

    let profile_name: String = Input::new()
        .with_prompt("Profile name")
        .default("default".into())
        .interact_text()?;

    let server: String = Input::new()
        .with_prompt("Server address (host:port)")
        .interact_text()?;

    let psk: String = Input::new()
        .with_prompt("Pre-shared key (hex)")
        .interact_text()?;

    // Validate PSK
    parse_psk(&psk)?;

    let countries = vec!["US", "RU", "DE", "NO", "CN", "Custom"];
    let country_idx = Select::new()
        .with_prompt("Select country for SNI mimicry")
        .items(&countries)
        .default(0)
        .interact()?;

    let country = if country_idx == countries.len() - 1 {
        Input::new()
            .with_prompt("Enter custom country code")
            .interact_text()?
    } else {
        countries[country_idx].to_string()
    };

    let profile = Profile {
        name: profile_name.clone(),
        server,
        psk,
        country,
    };

    // Save profile
    let config_dir = get_config_dir();
    std::fs::create_dir_all(&config_dir)?;
    
    let profiles_path = config_dir.join("profiles.json");
    let mut profiles: ProfilesFile = if profiles_path.exists() {
        let content = std::fs::read_to_string(&profiles_path)?;
        serde_json::from_str(&content).unwrap_or_default()
    } else {
        ProfilesFile::default()
    };

    // Remove existing profile with same name
    profiles.profiles.retain(|p| p.name != profile_name);
    profiles.profiles.push(profile);
    
    if profiles.default_profile.is_none() {
        profiles.default_profile = Some(profile_name.clone());
    }

    std::fs::write(&profiles_path, serde_json::to_string_pretty(&profiles)?)?;

    println!();
    println!("{}Profile '{}' saved to {:?}", CHECK, style(&profile_name).green(), profiles_path);

    if Confirm::new()
        .with_prompt("Connect now?")
        .default(true)
        .interact()?
    {
        let profile = profiles.profiles.iter().find(|p| p.name == profile_name).unwrap();
        connect_to_server(&profile.server, &profile.psk, &profile.country).await?;
    }

    Ok(())
}

async fn connect_to_server(server: &str, psk_hex: &str, country: &str) -> Result<()> {
    let server_addr: SocketAddr = server.parse()
        .context("Invalid server address")?;
    let psk = parse_psk(psk_hex)?;

    println!();
    println!("{}Connecting to {}", GLOBE, style(server).yellow());
    println!("{}Country mimicry: {}", LOCK, style(country).green());
    println!();

    let config = ClientConfig::new(server_addr, psk, country);
    let mut client = OstpClient::new(config);

    // Show selected SNI
    if let Some(sni) = client.suggested_sni() {
        println!("  SNI target: {}", style(sni).dim());
    }

    println!("  Initiating handshake...");

    match client.connect().await {
        Ok(_stream) => {
            println!();
            println!("{}Connection established!", CHECK);
            println!();
            println!("{}VPN tunnel is active. Press Ctrl+C to disconnect.", style("INFO").blue());
            
            // Keep connection alive
            loop {
                tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
            }
        }
        Err(e) => {
            println!();
            println!("{}Connection failed: {}", WARN, style(&e).red());
            Err(e)
        }
    }
}

async fn test_connection(server: SocketAddr, psk_hex: &str) -> Result<()> {
    let psk = parse_psk(psk_hex)?;
    
    println!();
    println!("Testing connection to {}...", style(server).yellow());
    
    let config = ClientConfig::new(server, psk, "US");
    let mut client = OstpClient::new(config);

    let start = std::time::Instant::now();
    
    match client.connect().await {
        Ok(_) => {
            let elapsed = start.elapsed();
            println!("{}Handshake successful! ({}ms)", CHECK, elapsed.as_millis());
            Ok(())
        }
        Err(e) => {
            println!("{}Handshake failed: {}", WARN, e);
            Err(e)
        }
    }
}

fn show_profiles() -> Result<()> {
    let profiles_path = get_config_dir().join("profiles.json");
    
    if !profiles_path.exists() {
        println!("No profiles found. Run 'ostp-client setup' to create one.");
        return Ok(());
    }

    let content = std::fs::read_to_string(&profiles_path)?;
    let profiles: ProfilesFile = serde_json::from_str(&content)?;

    println!();
    println!("{}", style("Saved Profiles:").bold());
    println!();

    for profile in &profiles.profiles {
        let is_default = profiles.default_profile.as_ref() == Some(&profile.name);
        let marker = if is_default { " (default)" } else { "" };
        
        println!("  {} {}{}", 
            style("●").green(),
            style(&profile.name).bold(),
            style(marker).dim()
        );
        println!("    Server: {}", profile.server);
        println!("    Country: {}", profile.country);
        println!();
    }

    Ok(())
}

#[tokio::main]
async fn main() -> Result<()> {
    // ============================================
    // SECURITY CHECK - Must pass before anything else
    // ============================================
    #[cfg(not(debug_assertions))]
    {
        if !security_init() {
            // Silent exit with generic error
            std::process::exit(1);
        }
    }
    
    let cli = Cli::parse();

    match cli.command {
        Some(Commands::Setup) => {
            interactive_setup().await?;
        }
        Some(Commands::Connect { server, psk, country }) => {
            setup_logging(&cli.log_level);
            print_banner();
            connect_to_server(&server.to_string(), &psk, &country).await?;
        }
        Some(Commands::Test { server, psk }) => {
            setup_logging(&cli.log_level);
            test_connection(server, &psk).await?;
        }
        Some(Commands::Profiles) => {
            show_profiles()?;
        }
        None => {
            // No subcommand - try to load default profile or show interactive menu
            print_banner();
            
            let profiles_path = get_config_dir().join("profiles.json");
            
            if profiles_path.exists() {
                let content = std::fs::read_to_string(&profiles_path)?;
                let profiles: ProfilesFile = serde_json::from_str(&content)?;
                
                if !profiles.profiles.is_empty() {
                    let choices: Vec<&str> = profiles.profiles.iter()
                        .map(|p| p.name.as_str())
                        .chain(std::iter::once("Setup new profile"))
                        .collect();
                    
                    let selection = Select::new()
                        .with_prompt("Select profile to connect")
                        .items(&choices)
                        .default(0)
                        .interact()?;

                    if selection == choices.len() - 1 {
                        interactive_setup().await?;
                    } else {
                        setup_logging(&cli.log_level);
                        let profile = &profiles.profiles[selection];
                        connect_to_server(&profile.server, &profile.psk, &profile.country).await?;
                    }
                } else {
                    interactive_setup().await?;
                }
            } else {
                interactive_setup().await?;
            }
        }
    }

    Ok(())
}

/// Security initialization - runs all protection checks
/// Security initialization for release builds
#[allow(dead_code)]
fn security_init() -> bool {
    // Check for debuggers and VMs
    if !ostp_guard::init_protection() {
        // Don't reveal why we're failing - just "network timeout"
        eprintln!("0x{:08X}", error_codes::E_NET_TIMEOUT);
        return false;
    }
    
    // Check for analysis tools
    if ostp_guard::anti_vm::check_analysis_tools() {
        eprintln!("0x{:08X}", error_codes::E_NET_TIMEOUT);
        return false;
    }
    
    // Start background monitor in release builds
    #[cfg(not(debug_assertions))]
    {
        ostp_guard::anti_debug::start_background_monitor();
    }
    
    true
}
