use anyhow::Context as _;
use clap::{Arg, ArgAction, ArgMatches, Command};
use reedline_repl_rs::Repl;
use s_macro::s;
use wildland_corex::api::{SpecialFileOperations, SpecialFileType};
use crate::{match_context, Context};
#[tracing::instrument(level = "trace", err(Debug), skip(_args, context))]
fn h_sfo_init(_args: ArgMatches, context: &mut Context) -> anyhow::Result<Option<String>> {
    match_context!(context, cargo => None, sfo => None)?;
    context.init_sfo("./sfo")?;
    Ok(Some(s!(
        "Special File Operations Service initialised with an empty registry"
    )))
}
#[tracing::instrument(level = "trace", err(Debug), skip(args, context))]
fn h_sfo_create_special_file(
    args: ArgMatches,
    context: &mut Context,
) -> anyhow::Result<Option<String>> {
    match_context!(context, sfo => Some(_))?;
    let file_name: String = args
        .get_one::<String>("file_name")
        .context("file name not provided")?
        .to_owned();
    let file_type: SpecialFileType = args
        .get_one::<String>("file_type")
        .context("file type not provided")?
        .to_owned()
        .try_into()?;
    let input_stream = None;
    context.sfo.as_mut().unwrap().create_special_file(
        file_name.to_owned(),
        file_type,
        input_stream,
    )?;
    Ok(Some(s!("Special File created: {}", file_name)))
}
#[tracing::instrument(level = "trace", err(Debug), skip(args, context))]
fn h_sfo_remove_special_file(
    args: ArgMatches,
    context: &mut Context,
) -> anyhow::Result<Option<String>> {
    match_context!(context, sfo => Some(_))?;
    let file_name: String = args
        .get_one::<String>("file_name")
        .context("file name not provided")?
        .to_owned();
    let file_type: SpecialFileType = args
        .get_one::<String>("file_type")
        .context("file type not provided")?
        .to_owned()
        .try_into()?;
    context
        .sfo
        .as_mut()
        .unwrap()
        .remove_special_file(file_name.to_owned(), file_type.to_owned())?;
    Ok(Some(s!(
        "Special File ({:?}) removed: {}",
        file_type,
        file_name
    )))
}
#[tracing::instrument(level = "trace", err(Debug), skip(context))]
fn h_sfo_get_special_file_path(
    args: ArgMatches,
    context: &mut Context,
) -> anyhow::Result<Option<String>> {
    match_context!(context, sfo => Some(_))?;
    let file_name: String = args
        .get_one::<String>("file_name")
        .context("file name not provided")?
        .to_owned();
    let file_type: SpecialFileType = args
        .get_one::<String>("file_type")
        .context("file type not provided")?
        .to_owned()
        .try_into()?;
    let file_path = context
        .sfo
        .as_ref()
        .unwrap()
        .get_special_file_path(file_name, file_type)?;
    Ok(Some(s!("Special file registry\n {:?}", file_path)))
}
#[tracing::instrument(level = "trace", err(Debug), skip(args, context))]
fn h_sfo_read_from_special_file(
    args: ArgMatches,
    context: &mut Context,
) -> anyhow::Result<Option<String>> {
    match_context!(context, sfo => Some(_))?;
    let file_name: String = args
        .get_one::<String>("file_name")
        .context("file name not provided")?
        .to_owned();
    let file_type: SpecialFileType = args
        .get_one::<String>("file_type")
        .context("file type not provided")?
        .to_owned()
        .try_into()?;
    let contents = context
        .sfo
        .as_mut()
        .unwrap()
        .read_from_special_file(file_name, file_type)?;
    if args
        .get_one::<bool>("interpret as ascii")
        .unwrap()
        .to_owned()
    {
        Ok(Some(s!(
            "Special File contents: {}",
            String::from_utf8(contents)?
        )))
    } else {
        Ok(Some(s!("Special File contents: {:?}", contents)))
    }
}
#[tracing::instrument(level = "trace", err(Debug), skip(args, context))]
fn h_sfo_write_to_special_file(
    args: ArgMatches,
    context: &mut Context,
) -> anyhow::Result<Option<String>> {
    match_context!(context, sfo => Some(_))?;
    let file_name: String = args
        .get_one::<String>("file_name")
        .context("file name not provided")?
        .to_owned();
    let file_type: SpecialFileType = args
        .get_one::<String>("file_type")
        .context("file type not provided")?
        .to_owned()
        .try_into()?;
    let truncate = args.get_one::<bool>("append").unwrap().to_owned();
    let new_contents = args
        .get_one::<String>("input")
        .context("input not provided")?
        .to_owned();
    context.sfo.as_mut().unwrap().write_to_special_file(
        file_name.to_owned(),
        file_type,
        new_contents.into_bytes(),
        truncate,
    )?;
    Ok(Some(s!("Data written to file: {}", file_name)))
}
pub fn extend(repl: Repl<Context, anyhow::Error>) -> Repl<Context, anyhow::Error> {
    repl.with_command(
        Command::new("sfo-init").about("Initialise SfoService"),
        h_sfo_init,
    )
    .with_command(
        Command::new("sfo-create")
            .about("Create a new special file")
            .arg(Arg::new("file_name").required(true))
            .arg(Arg::new("file_type").required(true)),
        h_sfo_create_special_file,
    )
    .with_command(
        Command::new("sfo-remove")
            .about("Remove a special file")
            .arg(Arg::new("file_name").required(true))
            .arg(Arg::new("file_type").required(true)),
        h_sfo_remove_special_file,
    )
    .with_command(
        Command::new("sfo-get-path")
            .arg(Arg::new("file_name").required(true))
            .arg(Arg::new("file_type").required(true))
            .about("Get the path of a special file"),
        h_sfo_get_special_file_path,
    )
    .with_command(
        Command::new("sfo-read")
            .about("Read a special file contents")
            .arg(Arg::new("file_name").required(true))
            .arg(Arg::new("file_type").required(true))
            .arg(
                Arg::new("interpret as ascii")
                    .action(ArgAction::SetTrue)
                    .short('a')
                    .long("ascii"),
            ),
        h_sfo_read_from_special_file,
    )
    .with_command(
        Command::new("sfo-write")
            .about("Write to a special file")
            .arg(Arg::new("file_name").required(true))
            .arg(Arg::new("file_type").required(true))
            .arg(Arg::new("input").required(true))
            .arg(
                Arg::new("append")
                    .action(ArgAction::SetTrue)
                    .short('a')
                    .long("append"),
            ),
        h_sfo_write_to_special_file,
    )
}