use anyhow::Context;
use log::info;
use std::{fs, path::Path};
use uuid::Uuid;

use crate::runners::{run_wait, CommandRun, RunReport};

#[derive(Debug)]
pub struct BcftoolsConfig {
    pub bin: String,
    pub threads: u8,
}

impl Default for BcftoolsConfig {
    fn default() -> Self {
        Self {
            bin: "/data/tools/bcftools-1.21/bcftools".to_string(),
            threads: 20,
        }
    }
}

pub fn bcftools_keep_pass(
    input: &str,
    output: &str,
    config: BcftoolsConfig,
) -> anyhow::Result<RunReport> {
    if !Path::new(input).exists() {
        anyhow::bail!("File doesnt exist {input}")
    }
    // First sort
    let tmp_file = format!("/tmp/{}", Uuid::new_v4());
    let mut cmd_run = CommandRun::new(&config.bin, &["sort", input, "-o", &tmp_file]);
    let _ = run_wait(&mut cmd_run)?;

    // 2. norm
    let tmp2_file = format!("/tmp/{}", Uuid::new_v4());
    let mut cmd_run = CommandRun::new(
        &config.bin,
        &[
            "norm",
            "--threads",
            &config.threads.to_string(),
            "-a",
            "--atom-overlaps",
            ".",
            &tmp_file,
            "-o",
            &tmp2_file,
        ],
    );
    let _ = run_wait(&mut cmd_run)?;
    fs::remove_file(tmp_file)?;

    // Then filter
    let mut cmd_run = CommandRun::new(
        &config.bin,
        &[
            "view",
            "--write-index",
            "--threads",
            &config.threads.to_string(),
            "-i",
            "FILTER='PASS'",
            &tmp2_file,
            "-o",
            output,
        ],
    );
    let res = run_wait(&mut cmd_run)?;
    fs::remove_file(tmp2_file)?;
    Ok(res)
}

pub fn bcftools_concat(
    inputs: Vec<String>,
    output: &str,
    config: BcftoolsConfig,
) -> anyhow::Result<RunReport> {
    info!("Concatening vcf with bcftools: {}", inputs.join(", "));
    let tmp_file = format!("/tmp/{}", Uuid::new_v4());
    fs::write(&tmp_file, inputs.join("\n"))?;

    let args = [
        "concat",
        "--write-index",
        "--threads",
        &config.threads.to_string(),
        "-a",
        "-D",
        "-f",
        &tmp_file,
        "-o",
        output,
    ];
    // Then filter
    let mut cmd_run = CommandRun::new(&config.bin, &args);
    let res = run_wait(&mut cmd_run)
        .context(format!("Error while running `bcftools {}`", args.join(" ")))?;
    fs::remove_file(tmp_file)?;
    Ok(res)
}

pub fn bcftools_keep_only_in_a(
    a: &str,
    b: &str,
    out: &str,
    config: &BcftoolsConfig,
) -> anyhow::Result<()> {
    let args = ["isec", "-C", "-w", "1", a, b, "-o", out];
    let mut cmd_run = CommandRun::new(&config.bin, &args);
    let _ = run_wait(&mut cmd_run)
        .context(format!("Error while running `bcftools {}`", args.join(" ")))?;
    Ok(())
}

pub fn bcftools_index(vcf: &str, config: &BcftoolsConfig) -> anyhow::Result<()> {
    let args = ["index", "--threads", &config.threads.to_string(), vcf];
    let mut cmd_run = CommandRun::new(&config.bin, &args);
    let _ = run_wait(&mut cmd_run)
        .context(format!("Error while running `bcftools {}`", args.join(" ")))?;
    Ok(())
}
