"""Test generic read_raw function."""

# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

from pathlib import Path
from shutil import copyfile

import pytest

from mne.datasets import testing
from mne.io import read_raw, read_raw_bci2k
from mne.io._read_raw import _get_readers, _get_supported, split_name_ext

base = Path(__file__).parents[1]
test_base = Path(testing.data_path(download=False))
bci2k_fname = test_base / "BCI2k" / "bci2k_test.dat"


@pytest.mark.parametrize("fname", ["x.xxx", "x"])
def test_read_raw_unsupported_single(fname):
    """Test handling of unsupported file types."""
    with pytest.raises(ValueError, match="Unsupported file type"):
        read_raw(fname)


@pytest.mark.parametrize("fname", ["x.bin"])
def test_read_raw_unsupported_multi(fname, tmp_path):
    """Test handling of supported file types but with bad data."""
    fname = tmp_path / fname
    fname.write_text("")
    with pytest.raises(RuntimeError, match="Could not read.*using any"):
        read_raw(fname)


@pytest.mark.parametrize("fname", ["x.vmrk", "y.amrk"])
def test_read_raw_suggested(fname):
    """Test handling of unsupported file types with suggested alternatives."""
    with pytest.raises(ValueError, match="Try reading"):
        read_raw(fname)


_testing_mark = testing._pytest_mark()


@pytest.mark.parametrize(
    "fname",
    [
        base / "tests/data/test_raw.fif",
        base / "tests/data/test_raw.fif.gz",
        base / "edf/tests/data/test.edf",
        pytest.param(
            base / "edf/tests/data/test.bdf",
            marks=(
                _testing_mark,
                pytest.mark.filterwarnings("ignore:Channels contain different"),
            ),
        ),
        base / "brainvision/tests/data/test.vhdr",
        base / "kit/tests/data/test.sqd",
        pytest.param(test_base / "KIT" / "data_berlin.con", marks=_testing_mark),
        pytest.param(
            test_base
            / "ARTEMIS123"
            / "Artemis_Data_2017-04-14-10h-38m-59s_Phantom_1k_HPI_1s.bin",
            marks=_testing_mark,
        ),
        pytest.param(
            test_base / "FIL" / "sub-noise_ses-001_task-noise220622_run-001_meg.bin",
            marks=(
                _testing_mark,
                pytest.mark.filterwarnings("ignore:.*problems later!:RuntimeWarning"),
            ),
        ),
    ],
)
def test_read_raw_supported(fname):
    """Test supported file types."""
    read_raw(fname)
    read_raw(fname, verbose=False)
    raw = read_raw(fname, preload=True)
    assert "data loaded" in str(raw)


def test_split_name_ext():
    """Test file name extension splitting."""
    # test known extensions
    for ext in _get_readers():
        assert split_name_ext(f"test{ext}")[1] == ext

    # test unsupported extensions
    for ext in ("this.is.not.supported", "a.b.c.d.e", "fif.gz.xyz"):
        assert split_name_ext(f"test{ext}")[1] is None


def test_read_raw_multiple_dots(tmp_path):
    """Test if file names with multiple dots work correctly."""
    src = base / "edf/tests/data/test.edf"
    dst = tmp_path / "test.this.file.edf"
    copyfile(src, dst)
    read_raw(dst)


reader_excluded_from_read_raw = {
    "read_raw_bti",
    "read_raw_hitachi",
    "read_raw_neuralynx",
}


def test_all_reader_documented():
    """Test that all the readers in the documentation are accepted by read_raw."""
    readers = _get_supported()
    # flatten the dictionaries and retrieve the function names
    functions = [foo.__name__ for value in readers.values() for foo in value.values()]
    # read documentation .rst source file
    doc_folder = Path(__file__).parents[3] / "doc"
    if not doc_folder.exists():
        pytest.skip("Documentation folder not found.")
    doc_file = doc_folder / "api" / "reading_raw_data.rst"
    doc = doc_file.read_text("utf-8")
    reader_lines = [
        line.strip() for line in doc.split("\n") if line.strip().startswith("read_raw_")
    ]
    reader_lines = [
        elt for elt in reader_lines if elt not in reader_excluded_from_read_raw
    ]
    missing_from_read_raw = set(reader_lines) - set(functions)
    missing_from_doc = set(functions) - set(reader_lines)
    if len(missing_from_doc) != 0 or len(missing_from_read_raw) != 0:
        raise AssertionError(
            "Functions missing from documentation of mne.io.read_raw:\n\t"
            + "\n\t".join(missing_from_doc)
            + "\n\nFunctions missing from read_raw:\n\t"
            + "\n\t".join(missing_from_read_raw)
        )
    if sorted(reader_lines) != list(reader_lines):
        raise AssertionError(
            "Functions in documentation are not sorted. Expected order:\n\t"
            + "\n\t".join(sorted(reader_lines))
        )


def test_all_reader_documented_in_docstring():
    """Test that all the readers are documented in read_raw docstring."""
    readers = _get_supported()
    # flatten the dictionaries and retrieve the function names
    functions = [foo.__name__ for value in readers.values() for foo in value.values()]
    doc = read_raw.__doc__.split("Parameters")[0]
    documented = [elt.strip().split("`")[0] for elt in doc.split("mne.io.")[1:]]
    missing_from_docstring = set(functions) - set(documented)
    if len(missing_from_docstring) != 0:
        raise AssertionError(
            "Functions missing from docstring:\n\t"
            + "\n\t".join(missing_from_docstring)
        )
    if sorted(documented) != documented:
        raise AssertionError("Functions in docstring are not sorted.")


@testing.requires_testing_data
def test_bci2k():
    """Test reading BCI2k files with read_raw."""
    read_raw_bci2k(bci2k_fname, preload=True)  # smoke test: if this works, below should
    raw = read_raw(bci2k_fname, preload=True)
    assert "RawBCI2k" in repr(raw)
