use detector::ort_ep;
use std::path::PathBuf;

#[derive(Debug, clap::Parser)]
pub struct Cli {
    #[clap(subcommand)]
    pub cmd: SubCommand,
}

#[derive(Debug, clap::Subcommand)]
pub enum SubCommand {
    #[clap(name = "detect")]
    Detect(Detect),
    #[clap(name = "detect-multi")]
    DetectMulti(DetectMulti),
    #[clap(name = "query")]
    Query(Query),
    #[clap(name = "similar")]
    Similar(Similar),
    #[clap(name = "stats")]
    Stats(Stats),
    #[clap(name = "compare")]
    Compare(Compare),
    #[clap(name = "gui")]
    Gui,
    #[clap(name = "completions")]
    Completions { shell: clap_complete::Shell },
}

#[derive(Debug, clap::ValueEnum, Clone, Copy, PartialEq)]
pub enum Models {
    RetinaFace,
    Yolo,
}

#[derive(Debug, Clone)]
pub enum Executor {
    Mnn(mnn::ForwardType),
    Ort(Vec<ort_ep::ExecutionProvider>),
}

#[derive(Debug, clap::Args)]
pub struct Detect {
    #[clap(short, long)]
    pub model: Option<PathBuf>,
    #[clap(short = 'M', long, default_value = "retina-face")]
    pub model_type: Models,
    #[clap(short, long)]
    pub output: Option<PathBuf>,
    #[clap(
        short = 'p',
        long,
        default_value = "cpu",
        group = "execution_provider",
        required_unless_present = "mnn_forward_type"
    )]
    pub ort_execution_provider: Vec<ort_ep::ExecutionProvider>,
    #[clap(
        short = 'f',
        long,
        group = "execution_provider",
        required_unless_present = "ort_execution_provider"
    )]
    pub mnn_forward_type: Option<mnn::ForwardType>,
    #[clap(short, long, default_value_t = 0.8)]
    pub threshold: f32,
    #[clap(short, long, default_value_t = 0.3)]
    pub nms_threshold: f32,
    #[clap(short, long, default_value_t = 8)]
    pub batch_size: usize,
    #[clap(short = 'd', long)]
    pub database: Option<PathBuf>,
    #[clap(long, default_value = "facenet")]
    pub model_name: String,
    #[clap(long)]
    pub save_to_db: bool,
    pub image: PathBuf,
}

#[derive(Debug, clap::Args)]
pub struct DetectMulti {
    #[clap(short, long)]
    pub model: Option<PathBuf>,
    #[clap(short = 'M', long, default_value = "retina-face")]
    pub model_type: Models,
    #[clap(short, long)]
    pub output_dir: Option<PathBuf>,
    #[clap(
        short = 'p',
        long,
        default_value = "cpu",
        group = "execution_provider",
        required_unless_present = "mnn_forward_type"
    )]
    pub ort_execution_provider: Vec<ort_ep::ExecutionProvider>,
    #[clap(
        short = 'f',
        long,
        group = "execution_provider",
        required_unless_present = "ort_execution_provider"
    )]
    pub mnn_forward_type: Option<mnn::ForwardType>,
    #[clap(short, long, default_value_t = 0.8)]
    pub threshold: f32,
    #[clap(short, long, default_value_t = 0.3)]
    pub nms_threshold: f32,
    #[clap(short, long, default_value_t = 8)]
    pub batch_size: usize,
    #[clap(short = 'd', long, default_value = "face_detections.db")]
    pub database: PathBuf,
    #[clap(long, default_value = "facenet")]
    pub model_name: String,
    #[clap(
        long,
        help = "Image extensions to process (e.g., jpg,png,jpeg)",
        default_value = "jpg,jpeg,png,bmp,tiff,webp"
    )]
    pub extensions: String,
    #[clap(help = "Directory containing images to process")]
    pub input_dir: PathBuf,
}

#[derive(Debug, clap::Args)]
pub struct Query {
    #[clap(short = 'd', long, default_value = "face_detections.db")]
    pub database: PathBuf,
    #[clap(short, long)]
    pub image_id: Option<i64>,
    #[clap(short, long)]
    pub face_id: Option<i64>,
    #[clap(long)]
    pub show_embeddings: bool,
    #[clap(long)]
    pub show_landmarks: bool,
}

#[derive(Debug, clap::Args)]
pub struct Similar {
    #[clap(short = 'd', long, default_value = "face_detections.db")]
    pub database: PathBuf,
    #[clap(short, long)]
    pub face_id: i64,
    #[clap(short, long, default_value_t = 0.7)]
    pub threshold: f32,
    #[clap(short, long, default_value_t = 10)]
    pub limit: usize,
}

#[derive(Debug, clap::Args)]
pub struct Stats {
    #[clap(short = 'd', long, default_value = "face_detections.db")]
    pub database: PathBuf,
}

#[derive(Debug, clap::Args)]
pub struct Compare {
    #[clap(short, long)]
    pub model: Option<PathBuf>,
    #[clap(short = 'M', long, default_value = "retina-face")]
    pub model_type: Models,
    #[clap(
        short = 'p',
        long,
        default_value = "cpu",
        group = "execution_provider",
        required_unless_present = "mnn_forward_type"
    )]
    pub ort_execution_provider: Vec<ort_ep::ExecutionProvider>,
    #[clap(
        short = 'f',
        long,
        group = "execution_provider",
        required_unless_present = "ort_execution_provider"
    )]
    pub mnn_forward_type: Option<mnn::ForwardType>,
    #[clap(short, long, default_value_t = 0.8)]
    pub threshold: f32,
    #[clap(short, long, default_value_t = 0.3)]
    pub nms_threshold: f32,
    #[clap(short, long, default_value_t = 8)]
    pub batch_size: usize,
    #[clap(long, default_value = "facenet")]
    pub model_name: String,
    #[clap(help = "First image to compare")]
    pub image1: PathBuf,
    #[clap(help = "Second image to compare")]
    pub image2: PathBuf,
}

impl Cli {
    pub fn completions(shell: clap_complete::Shell) {
        let mut command = <Cli as clap::CommandFactory>::command();
        clap_complete::generate(shell, &mut command, "detector", &mut std::io::stdout());
    }
}
