use crate::{
    collection::{HasOutputs, Initialize, Version}, commands::bcftools::{bcftools_keep_pass, BcftoolsConfig}, config::Config, helpers::force_or_not, runners::{run_wait, CommandRun, Run}
};
use anyhow::Context;
use std::{fs, path::Path};

#[derive(Debug)]
pub struct Savana {
    pub id: String,
    pub config: Config,
    pub log_dir: String,
}

impl Initialize for Savana {
    fn initialize(id: &str, config: Config) -> anyhow::Result<Self> {
        let log_dir = format!("{}/{}/log/savana", config.result_dir, id);
        if !Path::new(&log_dir).exists() {
            fs::create_dir_all(&log_dir)
                .context(format!("Failed  to create {log_dir} directory"))?;
        }

        Ok(Self {
            id: id.to_string(),
            config,
            log_dir,
        })
    }
}

impl Run for Savana {
    fn run(&mut self) -> anyhow::Result<()> {
        force_or_not(&self.config.savana_output_vcf(&self.id), self.config.savana_force)?;

        let id = &self.id;
        let savana_args = [
            // "run",
            "--tumour",
            &self.config.tumoral_haplotagged_bam(id),
            "--normal",
            &self.config.normal_haplotagged_bam(id),
            "--outdir",
            &self.config.savana_output_dir(id),
            "--ref",
            &self.config.reference,
            "--phased_vcf",
            &self.config.germline_phased_vcf(id),
            "--no_blacklist",
            "--threads",
            &self.config.savana_threads.to_string(),
        ];
        let args = [
            "-c",
            &format!(
                "source {} && conda activate savana && {} {}",
                self.config.conda_sh,
                self.config.savana_bin,
                savana_args.join(" ")
            ),
        ];
        let mut cmd_run = CommandRun::new("bash", &args);
        let report = run_wait(&mut cmd_run).context(format!(
            "Error while running `severus.py {}`",
            args.join(" ")
        ))?;

        let log_file = format!("{}/savana_", self.log_dir);
        report
            .save_to_file(&log_file)
            .context(format!("Error while writing logs into {log_file}"))?;

        // Keep PASS
        let passed_vcf = &self.config.savana_passed_vcf(id);
        let output_vcf = &self.config.savana_output_vcf(id);
        if !Path::new(passed_vcf).exists() && Path::new(output_vcf).exists() {
            let report = bcftools_keep_pass(output_vcf, passed_vcf, BcftoolsConfig::default())
                .context(format!(
                    "Error while running bcftools keep PASS for {output_vcf}"
                ))?;
            let log_file = format!("{}/bcftools_pass", self.log_dir);
            report
                .save_to_file(&log_file)
                .context(format!("Error while writing logs into {log_file}"))?;
        }
        Ok(())
    }
}

impl HasOutputs for Savana {
    fn has_output(&self, id: &str) -> (bool, bool) {
        let output = &self.config.savana_passed_vcf(id);
        let passed = &self.config.savana_passed_vcf(id);
        (Path::new(output).exists(), Path::new(passed).exists())
    }
}

impl Version for Savana {
    fn version(config: &Config) -> anyhow::Result<String> {
        let savana_args = ["--version"];
        let args = [
            "-c",
            &format!(
                "source {} && conda activate savana && {} {}",
                config.conda_sh,
                config.savana_bin,
                savana_args.join(" ")
            ),
        ];
        let mut cmd_run = CommandRun::new("bash", &args);
        let report = run_wait(&mut cmd_run)
            .context(format!("Error while running `savana {}`", args.join(" ")))?;
        let log = report.log;
        let start = log
            .find("Version ")
            .context("Failed to find 'Version ' in the log")?;
        let start_index = start + "Version ".len();
        let end = log[start_index..]
            .find('\n')
            .context("Failed to find newline after 'Version '")?;
        Ok(log[start_index..start_index + end]
            .to_string()
            .trim()
            .to_string())
    }
}

// impl LoadVariants for Savana {
//     fn load_variants(&self) -> anyhow::Result<Variants> {
//         let variants = read_vcf(self.savana_output_vcf, source, variant_type)?;
//         Ok()
//     }
// }
