"""
Integration tests for "real" data
"""

import glob
import os
import re
import shutil
import tarfile
from gzip import GzipFile

import pytest
from pkg_resources import resource_filename


def extract_test_data(tarball_path, outpath):
    """
    Extracts tar.gz data stored at `tarball_path` into `outpath`

    Parameters
    ----------
    tarball_path : str
        Path to the tar.gz file to extract
    outpath : str
        Path to directory where data should be extracted
    """
    with (
        open(tarball_path, "rb") as f,
        tarfile.open(fileobj=GzipFile(fileobj=f)) as t,
    ):
        os.makedirs(outpath, exist_ok=True)
        t.extractall(outpath)


def check_integration_outputs(fname, outpath, workflow="pySPFM"):
    """
    Checks outputs of integration tests (taken from tedana)
    Parameters
    ----------
    fname : str
        Path to file with expected outputs
    outpath : str
        Path to output directory generated from integration tests
    """

    # Gets filepaths generated by integration test
    existing = [
        os.path.relpath(f, outpath)
        for f in glob.glob(os.path.join(outpath, "**"), recursive=True)[1:]
    ]

    # Checks for log file
    if workflow == "pySPFM":
        log_regex = "^pySPFM_[12][0-9]{3}-[0-9]{2}-[0-9]{2}T[0-9]{2}[0-9]{2}[0-9]{2}.tsv$"
    elif workflow == "auc_to_estimates":
        log_regex = (
            "^auc_to_estimates_[12][0-9]{3}-[0-9]{2}-[0-9]{2}T[0-9]{2}[0-9]{2}[0-9]{2}.tsv$"
        )
    logfiles = [out for out in existing if re.match(log_regex, out)]
    assert len(logfiles) == 1

    # Removes logfile from list of existing files
    existing.remove(logfiles[0])

    # Compares remaining files with those expected
    with open(fname) as f:
        tocheck = f.read().splitlines()
    tocheck = [os.path.normpath(path) for path in tocheck]
    assert sorted(tocheck) == sorted(existing)


def test_integration_five_echo(
    skip_integration, script_runner, mask_five_echo, five_echo_data_tarball
):
    """Integration test of the full pySPFM workflow using single-echo data.

    Note: Multi-echo and BIDS output support is not yet implemented in the new CLI.
    This test uses single-echo data for basic functionality testing.
    """

    if skip_integration:
        pytest.skip("Skipping five-echo integration test")

    out_dir = "/tmp/data/five-echo/pySPFM.five-echo"

    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)

    # Extract the downloaded tarball and use the second echo
    extract_test_data(five_echo_data_tarball, os.path.dirname(out_dir))
    prepend = "/tmp/data/five-echo/p06.SBJ01_S09_Task11_e"
    suffix = ".psc.nii.gz"
    data = f"{prepend}2{suffix}"

    # CLI args using new subcommand format (single-echo, no BIDS)
    args = [
        "pySPFM",
        "sparse",
        "-i",
        data,
        "-m",
        mask_five_echo,
        "-o",
        "test-me",
        "--tr",
        "2",
        "-d",
        out_dir,
        "--criterion",
        "factor",
        "--factor",
        "10",
        "--max-iter",
        "50",
        "-j",
        "1",
        "--debug",
        "--debias",
    ]
    ret = script_runner.run(args)
    assert ret.success

    # Check that expected output files exist
    expected_files = [
        "_references.txt",
        "call.sh",
        "test-me_pySPFM_activityInducing.nii.gz",
        "test-me_pySPFM_denoised_bold.nii.gz",
        "test-me_pySPFM_lambda.nii.gz",
        "test-me_pySPFM_MAD.nii.gz",
    ]
    for f in expected_files:
        assert os.path.exists(os.path.join(out_dir, f)), f"Missing file: {f}"


def test_integration_lars(skip_integration, script_runner, mask_five_echo, five_echo_data_tarball):
    """Integration test of the full pySPFM workflow using five-echo test data."""

    if skip_integration:
        pytest.skip("Skipping five-echo integration test")

    out_dir = "/tmp/data/five-echo/pySPFM.five-echo"

    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)

    # Extract the downloaded tarball and use the second echo
    extract_test_data(five_echo_data_tarball, os.path.dirname(out_dir))
    prepend = "/tmp/data/five-echo/p06.SBJ01_S09_Task11_e"
    suffix = ".psc.nii.gz"
    data = f"{prepend}2{suffix}"

    # CLI args using new subcommand format
    args = [
        "pySPFM",
        "sparse",
        "-i",
        data,
        "-m",
        mask_five_echo,
        "-o",
        "test_lars",
        "--tr",
        "2",
        "-d",
        out_dir,
        "--criterion",
        "bic",
        "-j",
        "1",
        "--debug",
        "--debias",
    ]
    ret = script_runner.run(args)
    assert ret.success

    # Check that expected output files exist
    expected_files = [
        "_references.txt",
        "call.sh",
        "test_lars_pySPFM_activityInducing.nii.gz",
        "test_lars_pySPFM_denoised_bold.nii.gz",
        "test_lars_pySPFM_lambda.nii.gz",
        "test_lars_pySPFM_MAD.nii.gz",
    ]
    for f in expected_files:
        assert os.path.exists(os.path.join(out_dir, f)), f"Missing file: {f}"


def test_integration_stability_selection(
    skip_integration, script_runner, mask_five_echo, five_echo_data_tarball
):
    """Integration test of the pySPFM stability selection workflow using single-echo data."""

    if skip_integration:
        pytest.skip("Skipping five-echo integration test")

    out_dir = "/tmp/data/five-echo/pySPFM.five-echo"

    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)

    # Extract the downloaded tarball and use the second echo
    extract_test_data(five_echo_data_tarball, os.path.dirname(out_dir))
    prepend = "/tmp/data/five-echo/p06.SBJ01_S09_Task11_e"
    suffix = ".psc.nii.gz"
    data = f"{prepend}2{suffix}"

    # CLI args using new subcommand format (single-echo)
    args = [
        "pySPFM",
        "stability",
        "-i",
        data,
        "-m",
        mask_five_echo,
        "-o",
        "test_stability",
        "--tr",
        "2",
        "-d",
        out_dir,
        "-j",
        "1",
        "--debug",
    ]

    ret = script_runner.run(args)
    assert ret.success

    # Check that expected output files exist
    expected_files = [
        "_references.txt",
        "call.sh",
        "test_stability_pySPFM_AUC.nii.gz",
    ]
    for f in expected_files:
        assert os.path.exists(os.path.join(out_dir, f)), f"Missing file: {f}"


def test_integration_auc_to_estimates(
    skip_integration,
    script_runner,
    mask_five_echo,
    test_AUC,
    mean_AUC,
    auc_4D_thr,
    five_echo_data_tarball,
):
    if skip_integration:
        pytest.skip("Skipping five-echo integration test")

    out_dir = "/tmp/data/five-echo/pySPFM.five-echo"

    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)

    # Extract the downloaded tarball
    extract_test_data(five_echo_data_tarball, os.path.dirname(out_dir))
    prepend = "/tmp/data/five-echo/p06.SBJ01_S09_Task11_e"
    suffix = ".psc.nii.gz"
    datalist = [prepend + str(i + 1) + suffix for i in range(5)]
    echo_times = [15.4, 29.7, 44.0, 58.3, 72.6]

    ############################
    # CLI args for 95th percentile static thresholding
    args = [
        "auc_to_estimates",
        "-i",
        *datalist,
        "-te",
        *[str(te) for te in echo_times],
        "-m",
        mask_five_echo,
        mask_five_echo,
        "-a",
        test_AUC,
        "-thr",
        "95",
        "-o",
        "test_auc2est",
        "-tr",
        "2",
        "-d",
        out_dir,
        "-j",
        "1",
        "--debug",
    ]

    ret = script_runner.run(args)
    assert ret.success

    # compare the generated output files
    fn = resource_filename("pySPFM", "tests/data/auc_to_estimates_outputs.txt")
    check_integration_outputs(fn, out_dir, "auc_to_estimates")

    ############################
    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)
    # CLI args for 95th percentile dynamic thresholding
    args = [
        "auc_to_estimates",
        "-i",
        *datalist,
        "-te",
        *[str(te) for te in echo_times],
        "-m",
        mask_five_echo,
        mask_five_echo,
        "-a",
        test_AUC,
        "-thr",
        "95",
        "--strategy",
        "time",
        "-o",
        "test_auc2est",
        "-tr",
        "2",
        "-d",
        out_dir,
        "-j",
        "1",
        "--debug",
    ]

    ret = script_runner.run(args)
    assert ret.success

    # compare the generated output files
    fn = resource_filename("pySPFM", "tests/data/auc_to_estimates_outputs.txt")
    check_integration_outputs(fn, out_dir, "auc_to_estimates")

    ############################
    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)
    # CLI args for 3D mask thresholding
    args = [
        "auc_to_estimates",
        "-i",
        *datalist,
        "-te",
        *[str(te) for te in echo_times],
        "-m",
        mask_five_echo,
        mean_AUC,
        "-a",
        test_AUC,
        "-o",
        "test_auc2est",
        "-tr",
        "2",
        "-d",
        out_dir,
        "-j",
        "1",
        "--debug",
    ]

    ret = script_runner.run(args)
    assert ret.success

    # compare the generated output files
    fn = resource_filename("pySPFM", "tests/data/auc_to_estimates_outputs.txt")
    check_integration_outputs(fn, out_dir, "auc_to_estimates")

    ############################
    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)
    # CLI args for 4D mask thresholding
    args = [
        "auc_to_estimates",
        "-i",
        *datalist,
        "-te",
        *[str(te) for te in echo_times],
        "-m",
        mask_five_echo,
        auc_4D_thr,
        "-a",
        test_AUC,
        "-o",
        "test_auc2est",
        "-tr",
        "2",
        "-d",
        out_dir,
        "-j",
        "1",
        "--debug",
    ]

    ret = script_runner.run(args)
    assert ret.success

    # compare the generated output files
    fn = resource_filename("pySPFM", "tests/data/auc_to_estimates_outputs.txt")
    check_integration_outputs(fn, out_dir, "auc_to_estimates")


def test_integration_regressors(
    skip_integration, script_runner, mask_five_echo, five_echo_data_tarball
):
    """Integration test of the full pySPFM workflow using five-echo test data and regressors."""

    if skip_integration:
        pytest.skip("Skipping five-echo integration test")

    out_dir = "/tmp/data/five-echo/pySPFM.five-echo-regressors"

    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)

    # Extract the downloaded tarball and use the second echo
    extract_test_data(five_echo_data_tarball, os.path.dirname(out_dir))
    prepend = "/tmp/data/five-echo/p06.SBJ01_S09_Task11_e"
    suffix = ".psc.nii.gz"
    data = f"{prepend}2{suffix}"

    # Create regressors file
    regressors_fn = "/tmp/test_regressors.txt"
    import numpy as np

    np.random.seed(42)
    # The test data has 75 timepoints
    regs = np.random.randn(75, 3) * 0.1
    np.savetxt(regressors_fn, regs, fmt="%.6f")

    # CLI args using new subcommand format
    args = [
        "pySPFM",
        "sparse",
        "-i",
        data,
        "-m",
        mask_five_echo,
        "-o",
        "test-regressors",
        "--tr",
        "2",
        "-d",
        out_dir,
        "--criterion",
        "bic",
        "--regressors",
        regressors_fn,
        "-j",
        "1",
        "--debug",
        "--debias",
    ]
    ret = script_runner.run(args)
    if not ret.success:
        print(f"Return code: {ret.returncode}")
        print(f"Stdout: {ret.stdout}")
        print(f"Stderr: {ret.stderr}")
    assert ret.success

    # Check that output files exist
    expected_files = [
        "test-regressors_pySPFM_activityInducing.nii.gz",
        "test-regressors_pySPFM_denoised_bold.nii.gz",
        "test-regressors_pySPFM_lambda.nii.gz",
        "test-regressors_pySPFM_MAD.nii.gz",
    ]
    for f in expected_files:
        assert os.path.exists(os.path.join(out_dir, f))
