use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use anyhow::{Result, Context};

/// {{app_description}}
#[derive(Parser, Debug)]
#[command(name = "{{app_name}}")]
#[command(author, version, about, long_about = None)]
struct Cli {
    /// Config file path
    #[arg(short, long, value_name = "FILE")]
    config: Option<PathBuf>,

    /// Verbose mode
    #[arg(short, long, action = clap::ArgAction::Count)]
    verbose: u8,

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

#[derive(Subcommand, Debug)]
enum Commands {
    {{#each commands}}
    /// {{this.about}}
    {{pascal_case this.name}} {
        {{#each this.arguments}}
        /// {{this.help}}
        {{#if this.required}}
        #[arg(value_name = "{{upper this.name}}")]
        {{else}}
        #[arg(value_name = "{{upper this.name}}", default_value = "{{this.default}}")]
        {{/if}}
        {{this.name}}: {{this.rust_type}},
        {{/each}}

        {{#each this.options}}
        /// {{this.help}}
        #[arg({{#if this.short}}short = '{{this.short}}', {{/if}}long = "{{this.long}}"{{#if this.default}}, default_value = "{{this.default}}"{{/if}})]
        {{this.name}}: {{#if this.multiple}}Vec<{{this.rust_type}}>{{else}}{{this.rust_type}}{{/if}},
        {{/each}}
    },
    {{/each}}
}

{{#if config_file}}
#[derive(Debug, Deserialize, Serialize)]
struct Config {
    {{#each config_fields}}
    pub {{this.name}}: {{this.rust_type}},
    {{/each}}
}

impl Config {
    fn load(path: &PathBuf) -> Result<Self> {
        let content = std::fs::read_to_string(path)
            .context("Failed to read config file")?;

        {{#if (eq config_format "toml")}}
        toml::from_str(&content).context("Failed to parse TOML config")
        {{else if (eq config_format "yaml")}}
        serde_yaml::from_str(&content).context("Failed to parse YAML config")
        {{else if (eq config_format "json")}}
        serde_json::from_str(&content).context("Failed to parse JSON config")
        {{/if}}
    }
}
{{/if}}

fn main() -> Result<()> {
    let cli = Cli::parse();

    // Setup logging
    env_logger::Builder::new()
        .filter_level(match cli.verbose {
            0 => log::LevelFilter::Warn,
            1 => log::LevelFilter::Info,
            2 => log::LevelFilter::Debug,
            _ => log::LevelFilter::Trace,
        })
        .init();

    {{#if config_file}}
    // Load config if provided
    let config = if let Some(config_path) = &cli.config {
        Some(Config::load(config_path)?)
    } else {
        None
    };
    {{/if}}

    // Execute command
    match &cli.command {
        {{#each commands}}
        Commands::{{pascal_case this.name}} { {{#each this.arguments}}{{this.name}}, {{/each}}{{#each this.options}}{{this.name}}, {{/each}} } => {
            log::info!("Executing {{this.name}} command");
            {{#each this.validators}}
            validate_{{../name}}_{{this.field}}({{this.field}})?;
            {{/each}}
            execute_{{this.name}}({{#each this.arguments}}{{this.name}}, {{/each}}{{#each this.options}}{{this.name}}, {{/each}})?;
        }
        {{/each}}
    }

    Ok(())
}

{{#each commands}}
fn execute_{{this.name}}(
    {{#each this.arguments}}
    {{this.name}}: {{this.rust_type}},
    {{/each}}
    {{#each this.options}}
    {{this.name}}: {{#if this.multiple}}Vec<{{this.rust_type}}>{{else}}{{this.rust_type}}{{/if}},
    {{/each}}
) -> Result<()> {
    // TODO: Implement {{this.name}} command logic
    println!("{{this.about}}");
    {{#each this.arguments}}
    println!("  {{this.name}}: {:?}", {{this.name}});
    {{/each}}
    {{#each this.options}}
    println!("  {{this.name}}: {:?}", {{this.name}});
    {{/each}}
    Ok(())
}

{{#each this.validators}}
fn validate_{{../name}}_{{this.field}}(value: &{{this.rust_type}}) -> Result<()> {
    {{#if (eq this.type "range")}}
    if *value < {{this.min}} || *value > {{this.max}} {
        anyhow::bail!("{{this.error_message}}");
    }
    {{else if (eq this.type "regex")}}
    let re = regex::Regex::new(r"{{this.pattern}}")?;
    if !re.is_match(&value.to_string()) {
        anyhow::bail!("{{this.error_message}}");
    }
    {{/if}}
    Ok(())
}
{{/each}}
{{/each}}

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

    #[test]
    fn verify_cli() {
        use clap::CommandFactory;
        Cli::command().debug_assert()
    }
}
