use std::path::PathBuf;

use clap::{Args, Parser, Subcommand};
use sora_codegen::format::FormatMode;

#[derive(Debug, Parser)]
#[command(name = "sora")]
#[command(about = "Sora game configuration compiler")]
#[command(version)]
pub struct Cli {
    #[arg(long, global = true)]
    pub jobs: Option<usize>,

    #[arg(long, global = true)]
    pub serial: bool,

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

#[derive(Debug, Subcommand)]
pub enum Command {
    Build(BuildArgs),
    Check(CheckArgs),
    Init(InitArgs),
    Gen {
        #[arg(long)]
        target: String,

        #[command(flatten)]
        args: GenArgs,
    },
    Export(ExportArgs),
    Diff(DiffArgs),
    ExcelTemplate(ExcelTemplateArgs),
    SchemaLock(SchemaLockArgs),
}

#[derive(Debug, Args)]
pub struct CheckArgs {
    #[arg(long)]
    pub project: PathBuf,

    #[arg(long)]
    pub lock: Option<PathBuf>,
}

#[derive(Debug, Args)]
pub struct GenArgs {
    #[arg(long)]
    pub project: PathBuf,

    #[arg(long)]
    pub out: PathBuf,

    #[arg(long, value_enum, default_value_t = CodeFormatMode::Never)]
    pub format_code: CodeFormatMode,

    #[arg(long)]
    pub scope: Option<String>,
}

#[derive(Debug, Args)]
pub struct BuildArgs {
    #[arg(long)]
    pub project: PathBuf,

    #[arg(long, value_enum)]
    pub default_source_format: Option<SourceFormatArg>,

    #[arg(long)]
    pub data_root: Option<PathBuf>,

    #[arg(long)]
    pub scope: Option<String>,

    #[arg(long)]
    pub target: Vec<String>,

    #[arg(long)]
    pub clean: bool,
}

#[derive(Debug, Args)]
pub struct ExportArgs {
    #[arg(long)]
    pub format: String,

    #[arg(long, value_enum)]
    pub default_source_format: Option<SourceFormatArg>,

    #[arg(long)]
    pub project: PathBuf,

    #[arg(long)]
    pub data_root: PathBuf,

    #[arg(long)]
    pub out: PathBuf,

    #[arg(long)]
    pub scope: Option<String>,

    #[arg(long, value_enum, default_value = "none")]
    pub compression: ExportCompressionArg,

    #[arg(long)]
    pub compression_level: Option<i32>,
}

#[derive(Debug, Args)]
pub struct DiffArgs {
    #[arg(long, value_enum)]
    pub default_source_format: Option<SourceFormatArg>,

    #[arg(long)]
    pub project: PathBuf,

    #[arg(long)]
    pub left_root: PathBuf,

    #[arg(long)]
    pub right_root: PathBuf,

    #[arg(long)]
    pub out: PathBuf,

    #[arg(long)]
    pub scope: Option<String>,
}

#[derive(Debug, Args)]
pub struct InitArgs {
    #[arg(long)]
    pub out: PathBuf,

    #[arg(long, value_enum, default_value_t = SchemaFormatArg::Toml)]
    pub schema_format: SchemaFormatArg,

    #[arg(long)]
    pub force: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum SchemaFormatArg {
    Toml,
    Yaml,
    Json,
    Lua,
}

#[derive(Debug, Clone, Copy, clap::ValueEnum)]
pub enum SourceFormatArg {
    Csv,
    Toml,
    Xlsx,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum ExportCompressionArg {
    None,
    Zstd,
}

impl SourceFormatArg {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Csv => "csv",
            Self::Toml => "toml",
            Self::Xlsx => "xlsx",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, clap::ValueEnum)]
pub enum CodeFormatMode {
    #[default]
    Never,
    Auto,
    Required,
}

impl From<CodeFormatMode> for FormatMode {
    fn from(value: CodeFormatMode) -> Self {
        match value {
            CodeFormatMode::Never => Self::Never,
            CodeFormatMode::Auto => Self::Auto,
            CodeFormatMode::Required => Self::Required,
        }
    }
}

#[derive(Debug, Args)]
pub struct ExcelTemplateArgs {
    #[arg(long)]
    pub project: PathBuf,

    #[arg(long)]
    pub out: PathBuf,

    #[arg(long)]
    pub scope: Option<String>,
}

#[derive(Debug, Args)]
pub struct SchemaLockArgs {
    #[arg(long)]
    pub project: PathBuf,

    #[arg(long)]
    pub out: PathBuf,

    #[arg(long)]
    pub scope: Option<String>,
}
