use super::build::{trace_filter_parser, trace_level_parser};
use aiken_lang::ast::{TraceLevel, Tracing};
use aiken_project::{options::Options, watch::with_project};
use std::path::PathBuf;

#[derive(clap::Args)]
/// Export a function as a standalone UPLC program. Arguments to the function can be applied using
/// `aiken apply`.
pub struct Args {
    /// Path to project
    directory: Option<PathBuf>,

    /// Name of the function's module within the project
    #[clap(short, long)]
    module: String,

    /// Name of the function within the module
    #[clap(short, long)]
    name: String,

    /// Filter traces to be included in the generated program(s).
    ///
    ///   - user-defined:
    ///       only consider traces that you've explicitly defined
    ///       either through the 'trace' keyword of via the trace-if-false
    ///       ('?') operator.
    ///
    ///   - compiler-generated:
    ///       only included internal traces generated by the
    ///       Aiken compiler, for example in usage of 'expect'.
    ///
    ///   - all:
    ///       include both user-defined and compiler-generated traces.
    ///
    /// [optional] [default: all]
    #[clap(short = 'f', long, value_parser=trace_filter_parser(), default_missing_value="all", verbatim_doc_comment, alias = "filter_traces")]
    trace_filter: Option<fn(TraceLevel) -> Tracing>,

    /// Choose the verbosity level of traces:
    ///
    ///   - silent:
    ///       disable traces altogether
    ///
    ///   - compact:
    ///       only culprit line numbers are shown on failures
    ///
    ///   - verbose:
    ///       enable full verbose traces as provided by the user or the compiler
    ///
    /// [optional]
    #[clap(short, long, value_parser=trace_level_parser(), default_value_t=TraceLevel::Verbose, verbatim_doc_comment)]
    trace_level: TraceLevel,
}

pub fn exec(
    Args {
        directory,
        module,
        name,
        trace_filter,
        trace_level,
    }: Args,
) -> miette::Result<()> {
    with_project(directory.as_deref(), false, false, false, |p| {
        p.compile(Options::default())?;

        let export = p.export(
            &module,
            &name,
            match trace_filter {
                Some(trace_filter) => trace_filter(trace_level),
                None => Tracing::All(trace_level),
            },
        )?;

        let json = serde_json::to_string_pretty(&export).unwrap();

        println!("{}", json);

        Ok(())
    })
    .map_err(|_| std::process::exit(1))
}
