use crate::operations;
use anyhow::Result;
use clap::{Args, Parser, Subcommand, ValueEnum};

/// pipconf — Advanced pip configuration manager
/// Author: Hadi Cahyadi <cumulus13@gmail.com>
/// https://github.com/cumulus13/pipconf
#[derive(Parser, Debug)]
#[command(
    name = "pipconf",
    version = "1.0.0",
    author = "Hadi Cahyadi <cumulus13@gmail.com>",
    about = "Complete, advanced pip configuration file manager",
    long_about = "pipconf manages pip.ini / pip.conf files across all platforms.\n\
                  Supports auto-discovery, reading, editing, validation, backup,\n\
                  and full configuration for all known pip settings.\n\n\
                  Homepage: https://github.com/cumulus13/pipconf",
    after_help = "Run 'pipconf help <COMMAND>' for more information on a command.",
    propagate_version = true
)]
pub struct Cli {
    /// Override config file path (bypass auto-discovery)
    #[arg(short = 'f', long = "file", global = true, value_name = "PATH")]
    pub config_file: Option<String>,

    /// Output format
    #[arg(
        short = 'o',
        long = "output",
        global = true,
        default_value = "table",
        value_name = "FORMAT"
    )]
    pub output_format: OutputFormat,

    /// Suppress color output
    #[arg(long = "no-color", global = true)]
    pub no_color: bool,

    /// Verbose output
    #[arg(short = 'v', long = "verbose", global = true)]
    pub verbose: bool,

    #[command(subcommand)]
    pub command: Commands,
}

#[derive(ValueEnum, Debug, Clone)]
pub enum OutputFormat {
    Table,
    Json,
    Toml,
    Ini,
    Plain,
}

#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Show all discovered config file paths for this platform
    #[command(visible_alias = "find")]
    Discover(DiscoverArgs),

    /// Show current effective config (merged from all sources)
    #[command(visible_alias = "show")]
    List(ListArgs),

    /// Get the value of a specific key
    Get(GetArgs),

    /// Set a configuration value
    Set(SetArgs),

    /// Unset (remove) a configuration key
    Unset(UnsetArgs),

    /// Add a value to a list-type key (e.g. trusted-host, extra-index-url)
    Add(AddArgs),

    /// Remove a value from a list-type key
    Remove(RemoveArgs),

    /// Validate the config file(s) against all known pip settings
    #[command(visible_alias = "check")]
    Validate(ValidateArgs),

    /// Backup the config file
    Backup(BackupArgs),

    /// Restore from a backup
    Restore(RestoreArgs),

    /// Edit config in $EDITOR
    Edit(EditArgs),

    /// Print the config file path in use
    Path(PathArgs),

    /// Print all known pip configuration keys with docs
    #[command(visible_alias = "keys")]
    Schema,

    /// Initialize a new pip config file with sensible defaults
    Init(InitArgs),

    /// Reset a section or the whole config to defaults
    Reset(ResetArgs),

    /// Merge another config file into the current one
    Merge(MergeArgs),

    /// Diff two config files
    Diff(DiffArgs),

    /// Export config to various formats
    Export(ExportArgs),

    /// Import config from a file or stdin
    Import(ImportArgs),

    /// Show environment variables that affect pip
    Env,
}

#[derive(Args, Debug)]
pub struct DiscoverArgs {
    /// Show only existing files
    #[arg(short = 'e', long = "existing")]
    pub existing_only: bool,
}

#[derive(Args, Debug)]
pub struct ListArgs {
    /// Section to list (default: all)
    #[arg(short = 's', long = "section", value_name = "SECTION")]
    pub section: Option<String>,

    /// Show only keys that differ from defaults
    #[arg(long = "modified")]
    pub modified_only: bool,
}

#[derive(Args, Debug)]
pub struct GetArgs {
    /// Section (e.g. global, install, freeze)
    #[arg(short = 's', long = "section", default_value = "global")]
    pub section: String,

    /// Key name
    pub key: String,
}

#[derive(Args, Debug)]
pub struct SetArgs {
    /// Section
    #[arg(short = 's', long = "section", default_value = "global")]
    pub section: String,

    /// Key name
    pub key: String,

    /// Value
    pub value: String,

    /// Write to system-level config instead of user-level
    #[arg(long = "system")]
    pub system: bool,

    /// Write to site-level config (venv)
    #[arg(long = "site")]
    pub site: bool,
}

#[derive(Args, Debug)]
pub struct UnsetArgs {
    /// Section
    #[arg(short = 's', long = "section", default_value = "global")]
    pub section: String,

    /// Key name
    pub key: String,
}

#[derive(Args, Debug)]
pub struct AddArgs {
    /// Section
    #[arg(short = 's', long = "section", default_value = "global")]
    pub section: String,

    /// Key name (e.g. trusted-host, extra-index-url)
    pub key: String,

    /// Value to append
    pub value: String,
}

#[derive(Args, Debug)]
pub struct RemoveArgs {
    /// Section
    #[arg(short = 's', long = "section", default_value = "global")]
    pub section: String,

    /// Key name
    pub key: String,

    /// Value to remove
    pub value: String,
}

#[derive(Args, Debug)]
pub struct ValidateArgs {
    /// Also warn about unknown keys
    #[arg(long = "strict")]
    pub strict: bool,
}

#[derive(Args, Debug)]
pub struct BackupArgs {
    /// Backup file path (default: <config>.bak.<timestamp>)
    #[arg(short = 'd', long = "dest", value_name = "PATH")]
    pub dest: Option<String>,
}

#[derive(Args, Debug)]
pub struct RestoreArgs {
    /// Backup file to restore from
    pub backup_file: String,
}

#[derive(Args, Debug)]
pub struct EditArgs {
    /// Editor override (default: $EDITOR or vi)
    #[arg(short = 'e', long = "editor", value_name = "EDITOR")]
    pub editor: Option<String>,
}

#[derive(Args, Debug)]
pub struct PathArgs {
    /// Level: user, system, site, all
    #[arg(short = 'l', long = "level", default_value = "user")]
    pub level: ConfigLevel,
}

#[derive(ValueEnum, Debug, Clone)]
pub enum ConfigLevel {
    User,
    System,
    Site,
    All,
}

#[derive(Args, Debug)]
pub struct InitArgs {
    /// Use a mirror index (e.g. https://pypi.tuna.tsinghua.edu.cn/simple)
    #[arg(long = "index-url", value_name = "URL")]
    pub index_url: Option<String>,

    /// Add trusted host entries
    #[arg(long = "trusted-host", value_name = "HOST")]
    pub trusted_host: Vec<String>,

    /// Overwrite if exists
    #[arg(long = "force")]
    pub force: bool,
}

#[derive(Args, Debug)]
pub struct ResetArgs {
    /// Section to reset (omit to reset all)
    #[arg(short = 's', long = "section", value_name = "SECTION")]
    pub section: Option<String>,

    /// Skip confirmation prompt
    #[arg(long = "yes")]
    pub yes: bool,
}

#[derive(Args, Debug)]
pub struct MergeArgs {
    /// Source config file to merge from
    pub source: String,

    /// On conflict: keep existing, overwrite, or ask
    #[arg(long = "conflict", default_value = "ask")]
    pub conflict: ConflictStrategy,
}

#[derive(ValueEnum, Debug, Clone)]
pub enum ConflictStrategy {
    Keep,
    Overwrite,
    Ask,
}

#[derive(Args, Debug)]
pub struct DiffArgs {
    /// First config file (default: active config)
    pub file_a: Option<String>,

    /// Second config file
    pub file_b: String,
}

#[derive(Args, Debug)]
pub struct ExportArgs {
    /// Export format
    #[arg(short = 'f', long = "format", default_value = "ini")]
    pub format: ExportFormat,

    /// Output file (default: stdout)
    #[arg(short = 'o', long = "output", value_name = "FILE")]
    pub output: Option<String>,
}

#[derive(ValueEnum, Debug, Clone)]
pub enum ExportFormat {
    Ini,
    Json,
    Toml,
    Env,
    Shell,
}

#[derive(Args, Debug)]
pub struct ImportArgs {
    /// Input file (use '-' for stdin)
    pub input: String,

    /// Format of input file (auto-detect if omitted)
    #[arg(short = 'f', long = "format", value_name = "FORMAT")]
    pub format: Option<ExportFormat>,
}

pub fn run(cli: Cli) -> Result<()> {
    let ctx = operations::Context {
        config_file: cli.config_file.clone(),
        output_format: cli.output_format.clone(),
        no_color: cli.no_color,
        verbose: cli.verbose,
    };

    match cli.command {
        Commands::Discover(args) => operations::cmd_discover(&ctx, &args),
        Commands::List(args) => operations::cmd_list(&ctx, &args),
        Commands::Get(args) => operations::cmd_get(&ctx, &args),
        Commands::Set(args) => operations::cmd_set(&ctx, &args),
        Commands::Unset(args) => operations::cmd_unset(&ctx, &args),
        Commands::Add(args) => operations::cmd_add(&ctx, &args),
        Commands::Remove(args) => operations::cmd_remove(&ctx, &args),
        Commands::Validate(args) => operations::cmd_validate(&ctx, &args),
        Commands::Backup(args) => operations::cmd_backup(&ctx, &args),
        Commands::Restore(args) => operations::cmd_restore(&ctx, &args),
        Commands::Edit(args) => operations::cmd_edit(&ctx, &args),
        Commands::Path(args) => operations::cmd_path(&ctx, &args),
        Commands::Schema => operations::cmd_schema(&ctx),
        Commands::Init(args) => operations::cmd_init(&ctx, &args),
        Commands::Reset(args) => operations::cmd_reset(&ctx, &args),
        Commands::Merge(args) => operations::cmd_merge(&ctx, &args),
        Commands::Diff(args) => operations::cmd_diff(&ctx, &args),
        Commands::Export(args) => operations::cmd_export(&ctx, &args),
        Commands::Import(args) => operations::cmd_import(&ctx, &args),
        Commands::Env => operations::cmd_env(&ctx),
    }
}
