use clap::{Parser, Subcommand};
use sqlx::postgres::PgPoolOptions;
use std::time::Duration;
use std::sync::Arc;
use crate::repo::user_repo::UserRepository;
use crate::repo::space_repo::SpaceRepository;
use crate::repo::channel_repo::ChannelRepository;
use crate::repo::{UserRepo, SpaceRepo, ChannelRepo};
use argon2::{
    password_hash::{PasswordHasher, SaltString},
    Argon2,
};
use rand::rngs::OsRng;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Option<Commands>,
}

#[derive(Subcommand)]
pub enum Commands {
    /// First-time setup for the server
    Setup {
        /// Admin username
        #[arg(short, long)]
        username: String,

        /// Admin password
        #[arg(short, long)]
        password: String,

        /// Default space name
        #[arg(short, long, default_value = "Default Space")]
        space: String,

        /// Default channel name
        #[arg(short, long, default_value = "general")]
        channel: String,
    },
}

pub async fn handle_cli() -> bool {
    let cli = Cli::parse();

    match cli.command {
        Some(Commands::Setup { username, password, space, channel }) => {
            if let Err(e) = run_setup(username, password, space, channel).await {
                eprintln!("Setup failed: {}", e);
                std::process::exit(1);
            }
            println!("Setup completed successfully!");
            true
        }
        None => false,
    }
}

async fn run_setup(username: String, password: String, space_name: String, channel_name: String) -> Result<(), Box<dyn std::error::Error>> {
    dotenv::dotenv().ok();
    let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");

    let pool = PgPoolOptions::new()
        .max_connections(1)
        .acquire_timeout(Duration::from_secs(5))
        .connect(&db_url)
        .await?;

    let user_repo = UserRepository::new(pool.clone());
    let space_repo = SpaceRepository::new(pool.clone());
    let channel_repo = ChannelRepository::new(pool.clone());

    // 1. Create admin user
    println!("Creating admin user: {}...", username);
    
    let argon2 = Argon2::default();
    let salt = SaltString::generate(&mut OsRng);
    let passhash = argon2
        .hash_password(password.as_bytes(), &salt)
        .map_err(|e| e.to_string())?
        .to_string();

    let user_id = user_repo.new_user("admin@localhost", &username, &passhash).await?;
    user_repo.set_role(user_id, "admin").await?;

    // 2. Create default space
    println!("Creating default space: {}...", space_name);
    let space_id = space_repo.create(&space_name, Some("Default space created during setup"), user_id).await?;

    // 3. Create default channel
    println!("Creating default channel: {}...", channel_name);
    channel_repo.create(&channel_name, Some("Default channel"), space_id).await?;

    Ok(())
}
