import io
import os
import subprocess
import typing
import unittest.mock

import pandas
import pandas.util.testing
import pytest

import freesurfer_volume_reader
import freesurfer_volume_reader.__main__

from conftest import SUBJECTS_DIR, assert_volume_frames_equal


def assert_main_volume_frame_equals(capsys, argv: list, expected_frame: pandas.DataFrame,
                                    subjects_dir: typing.Optional[str] = None):
    if subjects_dir:
        os.environ['SUBJECTS_DIR'] = subjects_dir
    elif 'SUBJECTS_DIR' in os.environ:
        del os.environ['SUBJECTS_DIR']
    with unittest.mock.patch('sys.argv', [''] + argv):
        assert freesurfer_volume_reader.__main__.main() == 0
    out, _ = capsys.readouterr()
    resulted_frame = pandas.read_csv(io.StringIO(out)).drop(columns=['source_path'])
    if 'correction' in resulted_frame:
        resulted_frame['correction'] = resulted_frame['correction'].astype('object')
    assert_volume_frames_equal(
        left=expected_frame,
        # pandas.DataFrame.drop(columns=[...], ...) >= pandas0.21.0
        right=resulted_frame,
    )


@pytest.mark.parametrize(('args', 'root_dir_paths', 'expected_csv_path'), [
    ([],
     [os.path.join(SUBJECTS_DIR, 'alice')],
     os.path.join(SUBJECTS_DIR, 'alice', 'freesurfer-hippocampal-volumes.csv')),
    ([],
     [os.path.join(SUBJECTS_DIR, 'bert')],
     os.path.join(SUBJECTS_DIR, 'bert', 'freesurfer-hippocampal-volumes.csv')),
    ([],
     [os.path.join(SUBJECTS_DIR, 'alice'),
      os.path.join(SUBJECTS_DIR, 'bert')],
     os.path.join(SUBJECTS_DIR, 'freesurfer-hippocampal-volumes.csv')),
    ([],
     [SUBJECTS_DIR],
     os.path.join(SUBJECTS_DIR, 'freesurfer-hippocampal-volumes.csv')),
    (['--source-types', 'freesurfer-hipposf'],
     [os.path.join(SUBJECTS_DIR, 'alice')],
     os.path.join(SUBJECTS_DIR, 'alice', 'freesurfer-hippocampal-volumes.csv')),
    (['--source-types', 'freesurfer-hipposf'],
     [SUBJECTS_DIR],
     os.path.join(SUBJECTS_DIR, 'freesurfer-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     [os.path.join(SUBJECTS_DIR, 'alice')],
     os.path.join(SUBJECTS_DIR, 'alice', 'ashs-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     [os.path.join(SUBJECTS_DIR, 'bert')],
     os.path.join(SUBJECTS_DIR, 'bert', 'ashs-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     [os.path.join(SUBJECTS_DIR, 'alice'),
      os.path.join(SUBJECTS_DIR, 'bert')],
     os.path.join(SUBJECTS_DIR, 'ashs-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     [SUBJECTS_DIR],
     os.path.join(SUBJECTS_DIR, 'ashs-hippocampal-volumes.csv')),
    (['--source-types', 'ashs', 'freesurfer-hipposf'],
     [os.path.join(SUBJECTS_DIR, 'alice')],
     os.path.join(SUBJECTS_DIR, 'alice', 'all-hippocampal-volumes.csv')),
    (['--source-types', 'freesurfer-hipposf', 'ashs'],
     [os.path.join(SUBJECTS_DIR, 'alice')],
     os.path.join(SUBJECTS_DIR, 'alice', 'all-hippocampal-volumes.csv')),
    (['--source-types', 'ashs', 'freesurfer-hipposf'],
     [os.path.join(SUBJECTS_DIR, 'alice'),
      os.path.join(SUBJECTS_DIR, 'bert')],
     os.path.join(SUBJECTS_DIR, 'all-hippocampal-volumes.csv')),
    (['--source-types', 'ashs', 'freesurfer-hipposf'],
     [SUBJECTS_DIR],
     os.path.join(SUBJECTS_DIR, 'all-hippocampal-volumes.csv')),
])
def test_main_root_dir_param(capsys, args, root_dir_paths: list, expected_csv_path):
    assert_main_volume_frame_equals(
        argv=args + ['--'] + root_dir_paths,
        expected_frame=pandas.read_csv(expected_csv_path),
        capsys=capsys,
    )


@pytest.mark.parametrize(('args', 'root_dir_path', 'expected_csv_path'), [
    ([],
     SUBJECTS_DIR,
     os.path.join(SUBJECTS_DIR, 'freesurfer-hippocampal-volumes.csv')),
    ([],
     os.path.join(SUBJECTS_DIR, 'bert'),
     os.path.join(SUBJECTS_DIR, 'bert', 'freesurfer-hippocampal-volumes.csv')),
    (['--source-types', 'freesurfer-hipposf'],
     SUBJECTS_DIR,
     os.path.join(SUBJECTS_DIR, 'freesurfer-hippocampal-volumes.csv')),
    (['--source-types', 'freesurfer-hipposf'],
     os.path.join(SUBJECTS_DIR, 'bert'),
     os.path.join(SUBJECTS_DIR, 'bert', 'freesurfer-hippocampal-volumes.csv')),
    (['--source-types', 'freesurfer-hipposf'],
     os.path.join(SUBJECTS_DIR, 'bert', 'mri'),
     os.path.join(SUBJECTS_DIR, 'bert', 'freesurfer-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     SUBJECTS_DIR,
     os.path.join(SUBJECTS_DIR, 'ashs-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     os.path.join(SUBJECTS_DIR, 'bert'),
     os.path.join(SUBJECTS_DIR, 'bert', 'ashs-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     os.path.join(SUBJECTS_DIR, 'bert', 'final'),
     os.path.join(SUBJECTS_DIR, 'bert', 'ashs-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     os.path.join(SUBJECTS_DIR, 'alice'),
     os.path.join(SUBJECTS_DIR, 'alice', 'ashs-hippocampal-volumes.csv')),
    (['--source-types', 'ashs', 'freesurfer-hipposf'],
     os.path.join(SUBJECTS_DIR, 'alice'),
     os.path.join(SUBJECTS_DIR, 'alice', 'all-hippocampal-volumes.csv')),
    (['--source-types', 'freesurfer-hipposf', 'ashs'],
     os.path.join(SUBJECTS_DIR, 'alice'),
     os.path.join(SUBJECTS_DIR, 'alice', 'all-hippocampal-volumes.csv')),
    (['--source-types', 'freesurfer-hipposf', 'ashs'],
     SUBJECTS_DIR,
     os.path.join(SUBJECTS_DIR, 'all-hippocampal-volumes.csv')),
])
def test_main_root_dir_env(capsys, args, root_dir_path, expected_csv_path):
    assert_main_volume_frame_equals(
        argv=args,
        subjects_dir=root_dir_path,
        expected_frame=pandas.read_csv(expected_csv_path),
        capsys=capsys,
    )


@pytest.mark.timeout(8)
@pytest.mark.parametrize(('args', 'root_dir_path', 'subjects_dir', 'expected_csv_path'), [
    ([],
     os.path.join(SUBJECTS_DIR, 'bert'),
     os.path.join(SUBJECTS_DIR, 'alice'),
     os.path.join(SUBJECTS_DIR, 'bert', 'freesurfer-hippocampal-volumes.csv')),
    (['--source-types', 'ashs'],
     os.path.join(SUBJECTS_DIR, 'bert'),
     os.path.join(SUBJECTS_DIR, 'alice'),
     os.path.join(SUBJECTS_DIR, 'bert', 'ashs-hippocampal-volumes.csv')),
    ([],
     os.path.join(SUBJECTS_DIR, 'bert'),
     os.path.abspath(os.sep),
     os.path.join(SUBJECTS_DIR, 'bert', 'freesurfer-hippocampal-volumes.csv')),
])
def test_main_root_dir_overwrite_env(capsys, args, root_dir_path, subjects_dir, expected_csv_path):
    assert_main_volume_frame_equals(
        argv=args + ['--', root_dir_path],
        subjects_dir=subjects_dir,
        expected_frame=pandas.read_csv(expected_csv_path),
        capsys=capsys,
    )


def test_main_root_dir_filename_regex_freesurfer(capsys):
    expected_volume_frame = pandas.read_csv(
        os.path.join(SUBJECTS_DIR, 'bert', 'freesurfer-hippocampal-volumes.csv'))
    assert_main_volume_frame_equals(
        argv=['--freesurfer-hipposf-filename-regex', r'^.*-T1-T2\.v10\.txt$',
              os.path.join(SUBJECTS_DIR, 'bert')],
        expected_frame=expected_volume_frame[expected_volume_frame['analysis_id'] == 'T2'].copy(),
        capsys=capsys,
    )


def test_main_root_dir_filename_regex_ashs(capsys):
    expected_volume_frame = pandas.read_csv(
        os.path.join(SUBJECTS_DIR, 'bert', 'ashs-hippocampal-volumes.csv'))
    assert_main_volume_frame_equals(
        argv=['--ashs-filename-regex', r'_nogray_volumes.txt$',
              '--source-types', 'ashs', '--',
              os.path.join(SUBJECTS_DIR, 'bert')],
        expected_frame=expected_volume_frame[expected_volume_frame['correction']
                                             == 'nogray'].copy(),
        capsys=capsys,
    )


def test_main_root_dir_filename_regex_combined(capsys):
    expected_volume_frame = pandas.read_csv(
        os.path.join(SUBJECTS_DIR, 'alice', 'all-hippocampal-volumes.csv'))
    expected_volume_frame = expected_volume_frame[
        # pylint: disable=singleton-comparison
        (expected_volume_frame['T1_input'] == True)
        | ((expected_volume_frame['source_type'] == 'ashs')
           & expected_volume_frame['correction'].isnull())
    ]
    assert_main_volume_frame_equals(
        argv=['--ashs-filename-regex', r'^alice_left_heur_',
              '--freesurfer-hipposf-filename-regex', r'hippoSfVolumes-T1.v10.txt$',
              '--source-types', 'ashs', 'freesurfer-hipposf',
              '--', os.path.join(SUBJECTS_DIR, 'alice')],
        expected_frame=expected_volume_frame.copy(),
        capsys=capsys,
    )


def test_main_no_files_found(capsys):
    with unittest.mock.patch('sys.argv', ['', '--freesurfer-hipposf-filename-regex', r'^21$',
                                          '--', SUBJECTS_DIR]):
        assert os.EX_NOINPUT == freesurfer_volume_reader.__main__.main()
    out, err = capsys.readouterr()
    assert not out
    assert err == 'Did not find any volume files matching the specified criteria.\n'


def test_main_module_script_no_files_found():
    proc_info = subprocess.run(['python', '-m', 'freesurfer_volume_reader',
                                '--freesurfer-hipposf-filename-regex', r'^21$',
                                '--', SUBJECTS_DIR],
                               check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    assert os.EX_NOINPUT == proc_info.returncode
    assert not proc_info.stdout
    assert 'not find any volume files' in proc_info.stderr.rstrip().decode()


def test_script_no_files_found():
    proc_info = subprocess.run(['freesurfer-volume-reader',
                                '--freesurfer-hipposf-filename-regex', r'^21$',
                                '--', SUBJECTS_DIR],
                               check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    assert os.EX_NOINPUT == proc_info.returncode
    assert not proc_info.stdout
    assert 'not find any volume files' in proc_info.stderr.rstrip().decode()


def test_main_module_script_help():
    subprocess.run(['python', '-m', 'freesurfer_volume_reader', '--help'],
                   check=True)


def test_main_module_script_version():
    proc_info = subprocess.run(['python', '-m', 'freesurfer_volume_reader', '--version'],
                               check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    assert proc_info.stdout.rstrip() == freesurfer_volume_reader.__version__.encode()
    assert not proc_info.stderr