# SPDX-License-Identifier: AGPL-3.0-or-later
import io, os, gzip, queue, shutil, pathlib
import pytest

from parasplit.Read import read_fastq_gzip_simultaneously
from parasplit.WriteAndControl import open_output, write_pairs, manage_pigz_problems
from parasplit.Pretreatment import _split_two

requires_pigz = pytest.mark.skipif(shutil.which("pigz") is None, reason="pigz absent")

def _mk_fastq_gz(p: pathlib.Path, n=5, prefix="r"):
    with gzip.open(p, "wt") as f:
        for i in range(n):
            f.write(f"@{prefix}{i}/1\n")
            f.write("ACGT\n+\n!!!!\n")

def _count_reads_txt(p: pathlib.Path) -> int:
    with open(p, "rt", errors="ignore") as f:
        return sum(1 for _ in f)//4

@pytest.mark.parametrize("total,exp", [(2,(1,1)), (3,(1,2)), (5,(2,3))])
def test_split_two_properties(total, exp):
    a,b = _split_two(total)
    assert (a,b) == exp
    assert a>=1 and b>=1 and abs(a-b)<=1

@requires_pigz
def test_read_fastq_simultaneous_enqueues_blocks_and_sentinels(tmp_path):
    r1 = tmp_path/"R1.fq.gz"; r2 = tmp_path/"R2.fq.gz"
    _mk_fastq_gz(r1, n=7, prefix="A")
    _mk_fastq_gz(r2, n=7, prefix="B")

    q = queue.SimpleQueue()
    # num_threads total pour lecture; NumThreadFragmentation=3 → +3 sentinelles en finally
    read_fastq_gzip_simultaneously(str(r1), str(r2), q, num_threads=4, NumThreadFragmentation=3)

    # Draine la file
    stacks, sentinels = [], 0
    while not q.empty():
        it = q.get()
        if it is None: sentinels += 1
        else: stacks.append(it)

    # La fonction met 1 None avant le finally, puis +NumThreadFragmentation
    assert sentinels == 1 + 3

    # Déplie et vérifie format
    pairs = 0
    for block in stacks:
        assert isinstance(block, list)
        for trio in block:
            # trio = [[NomA, NomB], [seqA, seqB], [qualA, qualB]]
            assert isinstance(trio, list) and len(trio) == 3
            names, seqs, quals = trio
            assert all(isinstance(x, list) and len(x) == 2 for x in (names, seqs, quals))
            pairs += 1    # 7 paires attendues
    assert pairs == 7

@requires_pigz
def test_write_pairs_and_manage_pigz_problems_roundtrip(tmp_path):
    outF = tmp_path/"OF.fq.gz"
    outR = tmp_path/"OR.fq.gz"

    # 1) ouvre pigz en sortie avec partage des threads
    pF, pR = open_output(TWrite=4, output_forward=str(outF), output_reverse=str(outR))

    # 2) buffer simulé: 3 paires
    from queue import SimpleQueue
    q = SimpleQueue()
    def rec(name):  # enregistrements FASTQ minimaux
        return [f"@{name}\n", "AC\n", "+\n", "II\n"]
    q.put([rec("a/1"), rec("a/2")])
    q.put([rec("b/1"), rec("b/2")])
    q.put([rec("c/1"), rec("c/2")])

    # 3) envoie sentinelles pour TFrag=2
    q.put(None); q.put(None)

    # 4) écrit puis flush/close pigz proprement
    write_pairs(q, pF, pR, TFrag=2)
    manage_pigz_problems(pF, pR, str(outF), str(outR))

    # 5) vérifie contenu
    import gzip
    def nreads_gz(p):
        with gzip.open(p, "rt") as fh:
            return sum(1 for _ in fh)//4
    assert nreads_gz(outF) == 3
    assert nreads_gz(outR) == 3

def test_signal_handler_terminates(monkeypatch, capsys):
    from parasplit.WriteAndControl import signal_handler
    class Dummy:
        def __init__(self): self.k = 0
        def terminate(self): self.k += 1
    f = Dummy(); r = Dummy()
    with pytest.raises(SystemExit):
        signal_handler(2, None, f, r)
    assert f.k == 1 and r.k == 1
