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

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

impl Initialize for Severus {
    fn initialize(id: &str, config: Config) -> anyhow::Result<Self> {
        let mut output_vcf_exists = Path::new(&config.severus_output_vcf(id)).exists();
        if config.severus_force && output_vcf_exists {
            fs::remove_dir_all(config.severus_output_dir(id))?;
            output_vcf_exists = false;
        }

        if output_vcf_exists {
            anyhow::bail!("{} already exists.", config.severus_output_vcf(id))
        }

        let log_dir = format!("{}/{}/log/severus", config.result_dir, id);
        if !Path::new(&log_dir).exists() {
            fs::create_dir_all(&log_dir)
                .context(format!("Failed  to create {log_dir} directory"))?;
        }

        let phased_germline_vcf = &config.germline_phased_vcf(id);
        if !Path::new(phased_germline_vcf).exists() {
            anyhow::bail!("Could not find required phased VCF: {phased_germline_vcf}")
        }

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

impl Run for Severus {
    fn run(&mut self) -> anyhow::Result<()> {
        let id = &self.id;
        let output_vcf = &self.config.severus_output_vcf(id);
        let passed_vcf = &self.config.severus_passed_vcf(id);

        // Run command if output VCF doesn't exist
        let severus_args = [
            "--target-bam",
            &self.config.tumoral_bam(id),
            "--control-bam",
            &self.config.normal_bam(id),
            "--phasing-vcf",
            &self.config.germline_phased_vcf(id),
            "--out-dir",
            &self.config.severus_output_dir(id),
            "-t",
            &self.config.severus_threads.to_string(),
            "--write-alignments",
            "--use-supplementary-tag",
            "--resolve-overlaps",
            "--between-junction-ins",
            "--vntr-bed",
            &self.config.vntrs_bed,
        ];
        let args = [
            "-c",
            &format!(
                "source {} && conda activate severus_env && {} {}",
                self.config.conda_sh,
                self.config.severus_bin,
                severus_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!("{}/severus_", self.log_dir);
        report
            .save_to_file(&log_file)
            .context(format!("Error while writing logs into {log_file}"))?;

        // Keep PASS
        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 Severus {
    fn has_output(&self, id: &str) -> (bool, bool) {
        let output = &self.config.severus_passed_vcf(id);
        let passed = &self.config.severus_passed_vcf(id);
        (Path::new(output).exists(), Path::new(passed).exists())
    }
}

impl Version for Severus {
    fn version(config: &Config) -> anyhow::Result<String> {
        let args = [
            "-c",
            &format!(
                "source {} && conda activate severus_env && {} {}",
                config.conda_sh, config.severus_bin, "--version"
            ),
        ];
        let mut cmd_run = CommandRun::new("bash", &args);
        let report = run_wait(&mut cmd_run).context("Error while running `severus --version`")?;
        let v = match report.log.split_once(':') {
            Some((_, value)) => value.trim(),
            None => anyhow::bail!("Error while parsing `severus --version`"),
        };

        Ok(v.to_string())
    }
}

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

impl InitializeSolo for SeverusSolo {
    fn initialize(id: &str, time: &str, config: Config) -> anyhow::Result<Self> {
        let log_dir = format!("{}/{}/log/severus_solo", 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(SeverusSolo {
            id: id.to_string(),
            time: time.to_string(),
            config,
            log_dir,
        })
    }
    // add code here
}

impl Run for SeverusSolo {
    fn run(&mut self) -> anyhow::Result<()> {
        let id = &self.id;
        let time = &self.time;

        let output_vcf = &self.config.severus_solo_output_vcf(id, time);
        let passed_vcf = &self.config.severus_solo_passed_vcf(id, time);

        // Run command if output VCF doesn't exist
        let severus_args = [
            "--target-bam",
            &self.config.solo_bam(id, time),
            "--out-dir",
            &self.config.severus_solo_output_dir(id, time),
            "-t",
            &self.config.severus_threads.to_string(),
            "--write-alignments",
            "--use-supplementary-tag",
            "--resolve-overlaps",
            "--between-junction-ins",
            "--vntr-bed",
            &self.config.vntrs_bed,
        ];
        let args = [
            "-c",
            &format!(
                "source {} && conda activate severus_env && {} {}",
                self.config.conda_sh,
                self.config.severus_bin,
                severus_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!("{}/severus_", self.log_dir);
        report
            .save_to_file(&log_file)
            .context(format!("Error while writing logs into {log_file}"))?;

        // Keep PASS
        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(())
    }
}
