use std::ffi::{OsStr, OsString};
use std::fmt::Display;
use clap::{Arg, Command, Error};
use color_eyre::eyre::Result;
use regex::Regex;
use crate::plugins::PluginName;
use crate::toolset::ToolVersionRequest;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ToolArg {
    pub plugin: PluginName,
    pub tvr: Option<ToolVersionRequest>,
}
impl ToolArg {
    pub fn parse(input: &str) -> Self {
        match input.split_once('@') {
            Some((plugin, version)) => Self {
                plugin: plugin.to_string(),
                tvr: Some(ToolVersionRequest::new(plugin.to_string(), version)),
            },
            None => Self {
                plugin: input.into(),
                tvr: None,
            },
        }
    }
    pub fn double_tool_condition(tools: &[ToolArg]) -> Vec<ToolArg> {
        let mut tools = tools.to_vec();
        if tools.len() == 2 {
            let re: &Regex = regex!(r"^\d+(\.\d+)?(\.\d+)?$");
            let a = tools[0].clone();
            let b = tools[1].clone();
            if matches!(a.tvr, None) && matches!(b.tvr, None) && re.is_match(&b.plugin) {
                tools[1].tvr = Some(ToolVersionRequest::new(a.plugin.clone(), &b.plugin));
                tools[1].plugin = a.plugin;
                tools.remove(0);
            }
        }
        tools
    }
    pub fn with_version(self, version: &str) -> Self {
        Self {
            tvr: Some(ToolVersionRequest::new(self.plugin.clone(), version)),
            ..self
        }
    }
}
impl Display for ToolArg {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.tvr {
            Some(tvr) => write!(f, "{}", tvr),
            _ => write!(f, "{}", self.plugin),
        }
    }
}
#[derive(Debug, Clone)]
pub struct ToolArgParser;
impl clap::builder::TypedValueParser for ToolArgParser {
    type Value = ToolArg;
    fn parse_ref(
        &self,
        cmd: &Command,
        arg: Option<&Arg>,
        value: &OsStr,
    ) -> Result<Self::Value, Error> {
        self.parse(cmd, arg, value.to_os_string())
    }
    fn parse(
        &self,
        _cmd: &Command,
        _arg: Option<&Arg>,
        value: OsString,
    ) -> Result<Self::Value, Error> {
        Ok(ToolArg::parse(&value.to_string_lossy()))
    }
}