import os
import sys


# Resource scopes for Slurm execution.
resource_scopes:
    gpus="local",
    threads="local",
    vcpu="global",
    mem_mb="local",
    disk_mb="local",
    time="local",


# Config files are merged into Snakemake's global config dict in declaration
# order. Profile-specific settings come from config/day_profiles/<profile>/.
configfile: f"config/global.yaml"

# DAY_BIOME remains referenced by profile selection even though the workflow is
# otherwise profile-driven.
mg_biome = os.environ.get("DAY_BIOME", "AWSPC")
mg_profile = os.environ.get("DAY_PROFILE", "na")
if mg_biome in ["na"]:
    mg_biome = "AWSPC"

if mg_biome in ["na"] or mg_profile in ["na"]:
    print(
        "please run 'source dyoainit --project <PROJECT> ; day-activate (profile);'",
        file=sys.stderr,
    )

# Set workdir to the execution directory.
wd = os.path.abspath(".")
workdir: f"{wd}"


# TODO: Replace this try block with explicit profile config loading.
try:
    print(f"loading global: {wd}/config/global_{mg_biome}.yaml", file=sys.stderr)
    configfile: f"config/global_{mg_biome}.yaml"

    print(
        f"loading profile rule_config: {wd}/config/day_profiles/{mg_profile}/rule_config.yaml",
        file=sys.stderr,
    )
    configfile: f"config/day_profiles/{mg_profile}/rule_config.yaml"

    if config["valid_biome"] in [mg_biome]:
        pass
    else:
        print(
            f"The profile {mg_profile} is only valid in the compute environment "
            f"{config['valid_biome']}; you are running from {mg_biome}."
        )

except Exception as e:
    print("MISSING DAY_BIOME OR DAY_PROFILE", e, mg_biome)
    raise Exception(
        f"ERROR: {e}\n\nHave you executed: "
        "'source dyoainit --project <PROJECT> ; dy-a (profilename)'? "
        "Is the activated profile valid for this environment?"
    )


# Shared helpers used by rule modules.
include: "rules/global_common.smk"
include: "rules/help.smk"


day_init = """


   ______  _______ __   __        _____        __   __
   |     \ |_____|   \_/   |        |   |        \_/
   |_____/ |     |    |    |_____ __|__ |_____    |


|/////////////////////////////////////////////////////|
"""


##### SPECIAL RULES/DIRECTIVES: onstart, onsuccess, onerror
# ---------------------------------------------------------
# These hooks run around DAG execution. Staging and cleanup stay in explicit
# rules so resumed workflows do not rerun unrelated setup automatically.
# Snakemake also supports external workflow-status callbacks:
# https://snakemake.readthedocs.io/en/stable/executing/monitoring.html#

# ON START
onstart:
    os.system('echo "$(date) $PWD _begin_" >> DYMEMBERS')

    if config["pizzazz"] in ["on"] or random.randint(0, 9) in [1]:
        os.system("reset")
        print_colr_msg(day_init, "#FBC007", "#100D07", "b", "0.1")
        os.system("colr-run  -f bouncing_ball_rainbow -m '.' -- sleep 2 || sleep 1")

    msg = "               And we are off                  "
    wminm = get_term_width() - len(msg)
    tail = " " * wminm
    full = " " * int(get_term_width())
    msg2 = f"""{msg}{tail}"""
    if config["pizzazz"] in ["offX"]:
        print_colr_msg(
            msg2,
            os.environ.get("DY_WT0", "darkmagenta"),
            os.environ.get("DY_WB0", "lawngreen"),
            os.environ.get("DY_WS0", "b"),
            "2",
        )

    # If a Jira ticket ID is set with --config jid=RT-1000, update it at start.
    if config["jid"] != "":
        targets = "xxxxx"
        nsamps = len(samples)
        details = "."
        targets = "test"
        url = os.popen("echo $PWD").readline().rstrip()
        c1 = "FF0AFB"
        c2 = "828DED"
        c3 = "EEE7EE"
        update_jira(
            config["jid"],
            targets,
            nsamps,
            details,
            url,
            c1,
            c3,
            c3,
            samples.EX[0],
            samples.RU[0],
            "starting",
        )

onsuccess:
    nsamps_pass = "na"  # Calculate this at some point
    nsamps_fail = "na"  # and this
    os.system(
        'echo "$(date) $PWD COMPLETE, pass:'
        + nsamps_pass
        + ", fail:"
        + nsamps_fail
        + '" >> DYMEMBERS'
    )
    if "lock_analysis_dirs" in config:
        print("\n\n\t [[ INFO ]] ALL Directories Are Now Read Only. \n\n", file=sys.stderr)
        os.system("chmod -R +r-w ./* ;")
    else:
        print(
            "\n\n\tWARNING!!!! ALL Directories Are Remaining Unlocked. "
            "To have directories lock automatically upon successful completion, "
            "set --lock-analysis-dirs when calling day-run.\n\n",
            file=sys.stderr,
        )

    msg = "]]]]--------WORKFLOW SUCCESS====================]]]]]--"
    wminm = get_term_width() - len(msg)
    full = " " * int(get_term_width())
    tail = "-" * wminm
    msg2 = f"""{full}\n{msg}{tail}"""

    if config["pizzazz"] in ["on", "off"]:
        print_colr_msg(
            msg2,
            "black",
            "hotpink",
            "b",
            1.2,
        )

    # If a Jira ticket ID is set with --config jid=RT-1000, update it at success.
    if config["jid"] != "":
        targets = "xxxxx"
        nsamps = len(samples)
        details = "."
        targets = "test"
        url = os.popen("echo $PWD").readline().rstrip()
        c1 = "23FF0A"
        c2 = "00E038"
        c3 = "ADFFD3"
        update_jira(
            config["jid"],
            targets,
            nsamps,
            details,
            url,
            c1,
            c3,
            c3,
            samples.EX[0],
            samples.RU[0],
            "success",
        )

    # os.system(f"rm -rf /fsx/scratch/{config['tmpsub']} &")
    os._exit(0)


onerror:
    os.system('echo "$(date) $PWD FAIL" >> DYMEMBERS')
    msg = "        Womp Womp.  something went awry---- "
    wminm = get_term_width() - len(msg)
    tail = " " * wminm
    full = " " * int(get_term_width())
    msg2 = f"""{full}\n{msg}{tail}"""
    if config["pizzazz"] in ["on", "off"]:
        print_colr_msg(
            msg2,
            os.environ.get("DY_ET0", "firebrick3"),
            os.environ.get("DY_EB0", "midnightblue"),
            os.environ.get("DY_ES0", "f"),
            config["warn_err_sleep"],
        )
    # If a Jira ticket ID is set with --config jid=RT-1000, update it at failure.
    if config["jid"] != "":
        targets = "xxxxx"
        nsamps = len(samples)
        details = "."
        targets = "test"
        url = os.popen("echo $PWD").readline().rstrip()
        c1 = "FF4B3E"
        c2 = "FAB79E"
        c3 = "E8ECEE"
        update_jira(
            config["jid"],
            targets,
            nsamps,
            details,
            url,
            c1,
            c3,
            c3,
            samples.EX[0],
            samples.RU[0],
            "error",
        )

    # If Snakemake exits ungracefully, /fsx/scratch may need manual cleanup.
    # os.system(f"rm -rf /fsx/scratch/{config['tmpsub']} &")
    os._exit(1)


# Rule imports. Keep active includes alphabetized by rules/<name>.smk unless a
# rule must be loaded before a consumer that references its helpers or outputs.
# Disabled imports stay in their sorted position with the reason they remain off.
include: "rules/aivariant.smk"
include: "rules/alignstats.smk"
include: "rules/alignstats_compile.smk"
include: "rules/art.smk"
include: "rules/bcftools_vcfstat.smk"
include: "rules/bclconvert.smk"
include: "rules/bwa_mem2a_align_sort.smk"
include: "rules/calc_coverage_eveness.smk"
include: "rules/calc_coverage_evenness_two.smk"
include: "rules/clair3.smk"
include: "rules/legacy_cram_compat_bam.smk"
include: "rules/cyp2d6_cyrius.smk"
include: "rules/deepsomatic.smk"
include: "rules/deepvariant_1_5.smk"
include: "rules/deepvariant_1_9.smk"
include: "rules/deepvariant_1_9_roche.smk"
include: "rules/deepvariant_ug.smk"
include: "rules/doppel_mrkdups.smk"
include: "rules/duphold.smk"
include: "rules/dysgu_sv.smk"
include: "rules/evidence_manifest.smk"
include: "rules/expansionhunter.smk"
include: "rules/fastqc.smk"
# FASTV is retired from active Snakemake execution; archived at
# workflow/rules/archived_qc/fastv.smk for old-run provenance.
include: "rules/gatk_contam.smk"
# Gauchian is disabled until its runtime environment path and caller behavior
# are revalidated. Keep workflow/rules/gauchian.smk for repair provenance.
# include: "rules/gauchian.smk"
# include: "rules/gba_gauchian.smk"  # Disabled until GBA integration is revalidated.
include: "rules/generate_deduplicated_bams.smk"
# GeneToCN is disabled until its upstream package/install surface is revalidated.
# Keep workflow/rules/genetocn.smk for repair provenance.
# include: "rules/genetocn.smk"
include: "rules/go_left.smk"
# include: "rules/hisat2.smk"  # Disabled until HISAT2 workflows are revalidated.
include: "rules/htd_calls.smk"
include: "rules/hyb_ensemble_multi_platform.smk"
# KAT is too slow/noisy for routine service and requires explicit revalidation.
# include: "rules/kat.smk"
include: "rules/lofreq2.smk"
include: "rules/longtr.smk"
include: "rules/manta.smk"
include: "rules/merge_all_bams.smk"
include: "rules/mosdepth.smk"
include: "rules/multiqc_cov_aln.smk"
include: "rules/multiqc_final_wgs.smk"
include: "rules/multiqc_for_raw_fastqs.smk"
include: "rules/multiqc_singleton.smk"
include: "rules/mutect2.smk"
include: "rules/no_dedup.smk"
include: "rules/no_dedup_cram.smk"
include: "rules/octopus.smk"
# Parascopy is disabled until locus configuration assets and runtime behavior
# are revalidated. Keep workflow/rules/parascopy.smk for repair provenance.
# include: "rules/parascopy.smk"
include: "rules/peddy.smk"
# Picard/htsjdk cannot reliably read current short-read CRAM 3.1 outputs.
# include: "rules/picard.smk"
include: "rules/prep_input_sample_files.smk"
# Qualimap is too slow and unreliable for routine service on current CRAM/BAMs.
# include: "rules/qualimap.smk"
include: "rules/quickmer2.smk"
include: "rules/relatedness_batch.smk"
include: "rules/roche_sbxd.smk"
include: "rules/rtg_vcfeval.smk"
include: "rules/rtg_vcfstats.smk"
include: "rules/run_qc_reports.smk"
include: "rules/samtools_metrics.smk"
include: "rules/sent_aln_sort_snv.smk"
include: "rules/sent_DNAscope.smk"
include: "rules/sent_DNAscope_cgt7p.smk"
include: "rules/sent_hybrid_ilmn_ont_modular.refactored.smk"
include: "rules/sent_hybrid_ilmn_pb_modular.refactored.smk"
include: "rules/sent_hybrid_ug_ont_modular.refactored.smk"
include: "rules/sent_hybrid_ug_pb_modular.refactored.smk"
include: "rules/sent_snv_ont.smk"
include: "rules/sent_snv_pb.smk"
include: "rules/sent_snv_ug.smk"
include: "rules/sent_TNscope.smk"
include: "rules/sentieon.smk"
include: "rules/sentieon_gatk.smk"
include: "rules/sentieon_markdups.smk"
include: "rules/sentieon_new_gatk.smk"
include: "rules/sentieon_pangenome_shortreads.smk"
include: "rules/sentieon_pangenome_ug.smk"
include: "rules/sentmm2_align_sort.smk"
include: "rules/sentmm2ont_align_sort.smk"
include: "rules/seqfu.smk"
include: "rules/site_mix_contam.smk"
include: "rules/contam_identity.smk"
# SMACA is disabled until its runtime environment path and caller behavior
# are revalidated. Keep workflow/rules/smaca.smk for repair provenance.
# include: "rules/smaca.smk"
# SMN12 is disabled until its runtime environment path and caller behavior
# are revalidated. Keep workflow/rules/smn_copynumbercaller.smk for repair provenance.
# include: "rules/smn_copynumbercaller.smk"
# SnpEff is unavailable until annotation integration is revalidated.
# include: "rules/snpeff.smk"
# Stargazer is intentionally excluded from htd_callers until its stale rule is repaired.
# include: "rules/stargazer.smk"
include: "rules/strobe_align_sort.smk"
include: "rules/surveyor.smk"
include: "rules/tiddit.smk"
include: "rules/truvari_sv_benchmark.smk"
include: "rules/unmapped_metagenomics.smk"
include: "rules/varnet.smk"
include: "rules/vep.smk"
# VerifyBamID2 is retired from active Snakemake execution; archived at
# workflow/rules/archived_qc/verifybamid2_contam.smk for old-run provenance.
include: "rules/vpot.smk"
include: "rules/workflow_staging.smk"
include: "rules/workflow_target_aliases.smk"


# #### A FEW FUSSY THINGS
# ----------------------
# Snakemake does not like double slashes in paths and prefers no leading "./".

# #### RULES, SPECIFICALLY 'all' (replaced by day here)
# "all" is the common full-pipeline rule name. Daylily has multiple pipeline
# targets instead; discover them with "dy-r help" or shell completion.


# localrules: comma-separated rules that run on the headnode. Use this only for
# low-compute coordination rules that should not be submitted to workers.

localrules:
    day,
    all,


rule day:  # TARGET: Synonym for all
    "Does nothing but touch the output file. To run a useful rule, run 'dy-r help'."
    output:
        "logs/day.completed",
    log:
        "logs/day.log"
    benchmark:
        "logs/benchmarks/day.bench.tsv"
    shell:
        "touch {output};"

rule all:  # TARGET: Prompt user to run help
    log:
        "logs/all.log"
    benchmark:
        "logs/benchmarks/all.bench.tsv"
    shell:
        """
        echo 'Hello!  To learn how to run daylily, execute the following:' ;
        echo ' ';
        echo ' ';
        echo 'source dyoainit  --project <PROJECT> ';
        echo 'dy-a local/slurm';
        echo 'dy-r help';
        """


# #### AND THAT IS THE SNAKEFILE
# ------------------------------
