use crate::cmds::accounts::get_current_account;
use clap::Subcommand;
use ordinary_admin::client::AdminClient;
use serde_json::Value;
use std::error::Error;

#[derive(Clone)]
pub enum UuidVersion {
    /// random
    V4,
    /// unix timestamp
    V7,
}

impl UuidVersion {
    fn as_str(&self) -> &'static str {
        match self {
            Self::V4 => "v4",
            Self::V7 => "v7",
        }
    }
}

impl clap::ValueEnum for UuidVersion {
    fn value_variants<'a>() -> &'a [Self] {
        &[Self::V4, Self::V7]
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        match self {
            Self::V4 => Some(clap::builder::PossibleValue::new("v4")),
            Self::V7 => Some(clap::builder::PossibleValue::new("v7")),
        }
    }
}

#[derive(Subcommand)]
pub enum Models {
    /// add a new model to your Ordinary project
    Add {
        /// name of the model
        name: String,

        /// UUID version
        uuid_version: Option<UuidVersion>,
    },

    /// manage model items for the application running on an `ordinaryd` instance
    Items {
        #[command(subcommand)]
        item: Items,
    },
}

#[derive(Subcommand)]
pub enum Items {
    /// list model items for the application running on an `ordinaryd` instance
    List {
        /// name of the model
        name: String,

        /// for applications that need to consume stdio or pipe to `jq`
        #[arg(long, default_value_t = false)]
        json: bool,
    },
}

impl Models {
    pub async fn handle(
        &self,
        host_domain: Option<&str>,
        accept_invalid_certs: bool,
        project: &str,
        insecure: bool,
    ) -> Result<(), Box<dyn Error>> {
        let account = get_current_account(insecure)?;
        let client = AdminClient::new(
            &account.host,
            &account.name,
            host_domain,
            accept_invalid_certs,
        );

        match self {
            Self::Add { name, uuid_version } => {
                ordinary_modify::add_model(
                    project,
                    name,
                    uuid_version.to_owned().map(|uv| uv.as_str()),
                )?;
            }
            Self::Items { item } => match item {
                Items::List { name, json } => {
                    let res = client.items_list(project, name).await?;

                    if json == &true {
                        print!("{res}");
                    } else {
                        let items: Vec<Value> = serde_json::from_str(&res)?;

                        for item in items {
                            tracing::info!(%item);
                        }
                    }
                }
            },
        }

        Ok(())
    }
}
