# =======================
# Python internal package
# =======================
# D
import dataclasses

# P
import pathlib

# T
import tempfile
import textwrap

# ================
# External package
# ================
# P
import pytest

# ==============
# Module package
# ==============
# P
from src.strange.parser.configuration import (
    Configuration,
)


@dataclasses.dataclass
class ConfigurationFile:
    """A dataclass to simulate possible configuration file."""

    good__yaml: str = """\
    miscellaneous:
        csv_separator: ";"
    """

    good__json: str = """\
    {
        "miscellaneous": {
            "csv_separator": ";"
        }
    }
    """

    good__toml: str = """\
    [miscellaneous]
    csv_separator = ";"
    """

    bad__yaml: str = """\
    miscellaneous::
    """

    bad__json: str = """\
    "miscellaneous": {
    """

    bad__toml: str = """\
    [miscellaneous
    """

    wrong_global_key__yaml: str = """\
    miscellaneou:
        csv_separator: ";"
    """

    wrong_specific_key__yaml: str = """\
    miscellaneous:
        csv: ";"
    """


@pytest.fixture
def __configuration_file() -> ConfigurationFile:
    """Pytest feature that return construct dataclass with configuration file."""
    return ConfigurationFile()


@pytest.fixture
def __registry_validation_handler():
    """Return a registry to call good test."""
    return {
        "good": __good_configuration,
        "bad": __bad_configuration,
        "wrong_global_key": __wrong_global_key_configuration,
        "wrong_specific_key": __wrong_specific_key_configuration,
    }


@pytest.mark.parametrize("field", dataclasses.fields(ConfigurationFile()))
def test_configuration(
    __configuration_file: ConfigurationFile,
    __registry_validation_handler,
    field: dataclasses.Field,
) -> None:
    """Test configuration file validation for different supported file formats.

    Parameters
    ----------
    __configuration_file : `ConfigurationFile`
        A fixture providing various configuration file templates or serialized
        examples.

    __registry_validation_handler : `dict[str, Callable]`
        A mapping between configuration file types and their corresponding
        validation functions.

    field : `dataclasses.Field`
        A dataclass field from `ConfigurationFile`, representing one
        configuration format to test.
    """
    file_extension: str = "." + field.name.split("__")[-1]

    with tempfile.NamedTemporaryFile(
        "w+", suffix=file_extension, delete=True
    ) as file:
        _ = file.write(
            textwrap.dedent(getattr(__configuration_file, field.name))
        )

        file.flush()
        file.seek(0)

        path: pathlib.Path = pathlib.Path(file.name).expanduser()
        __registry_validation_handler[field.name.split("__")[0]](path)


def __good_configuration(path: pathlib.Path) -> None:
    """Validate that a configuration file can be successfully parsed.

    Parameters
    ----------
    path : `pathlib.Path`
        The path to the configuration file.
    """
    _ = Configuration(path)


def __bad_configuration(path: pathlib.Path) -> None:
    """Validate that a configuration file cannot be parsed.

    Parameters
    ----------
    path : `pathlib.Path`
        The path to the configuration file.
    """
    with pytest.raises(ValueError):
        _ = Configuration(path)


def __wrong_global_key_configuration(path: pathlib.Path) -> None:
    """Validate that a configuration file cannot be parsed, due to a wrong
    global configuration key.

    Parameters
    ----------
    path : `pathlib.Path`
        The path to the configuration file.
    """
    try:
        _ = Configuration(path)
    except ValueError as error:
        assert (
            "Keys '[miscellaneou]' should not be present inside the given "
            + "configuration."
            in str(error)
        )
    else:
        assert False


def __wrong_specific_key_configuration(path: pathlib.Path) -> None:
    """Validate that a configuration file cannot be parsed, due to a wrong
    specific key.

    Parameters
    ----------
    path : `pathlib.Path`
        The path to the configuration file.
    """
    try:
        _ = Configuration(path)
    except ValueError as error:
        assert "For 'miscellaneous', key 'csv' unmatched." in str(error)
    else:
        assert False
