"""
Example Code for a blog post on juergen.rocks
=============================================

**Module:** ``tests.test_blog_example``

This is a test example for
`a blog post on juergen.rocks <https://juergen.rocks/blog/articles/data-driven-tests-mit-pytest-csv-params.html>`_.

The example consists of serval helper methods and a lot of configuration for the
:meth:`~pytest_csv_params.decorator.csv_params` decorator.

The CSV file looks like this:

.. literalinclude:: ../../../tests/assets/blog-example.csv
   :language: text

You find the CSV file in ``tests/assets/blog-example.csv``.
"""
import re
from os.path import dirname, join

from pytest_csv_params.decorator import csv_params


def get_volume(size_data: str) -> int:
    """
    Get the volume from size data, return it as mm³.

    Helper method, will be used as a data caster.

    :param size_data: String from the CSV file.
    :returns: Volume in mm³
    :raises: ValueError: When the test data cannot be converted
    """
    matcher = re.compile(r"^\D*(?P<l>\d+)\D+(?P<d>\d+)\D+(?P<h>\d+)\D+$").match(size_data)
    if matcher is None:
        raise ValueError("Bad Test Data") from None
    return int(matcher.group("l")) * int(matcher.group("d")) * int(matcher.group("h"))


def get_container_volume(container_size: str) -> int:
    """
    Get the container size (remove the unit, as mm³).

    Helper method, will be used as a data caster.

    :param container_size: String from the CSV file.
    :returns: Volume of the container in mm³
    :raises: ValueError: When the test data cannot be converted
    """

    matcher = re.compile(r"^\D*(?P<size>\d+)\D+$").match(container_size)
    if matcher is None:
        raise ValueError("Bad Test Data") from None
    return int(matcher.group("size")) * 1_000_000_000


@csv_params(
    data_file=join(dirname(__file__), "assets", "blog-example.csv"),
    id_col="Order-Ref #",
    header_renames={
        "Anz. Schrauben-Päck.": "anz_schrauben",
        "Dim. Schrauben-Päck.": "vol_schrauben",
        "Anz. Scheiben-Päck.": "anz_scheiben",
        "Dim. Scheiben-Päck.": "vol_scheiben",
        "Volumen Container": "vol_container",
    },
    data_casts={
        "anz_schrauben": int,
        "anz_scheiben": int,
        "vol_schrauben": get_volume,
        "vol_scheiben": get_volume,
        "vol_container": get_container_volume,
    },
)
def test_does_it_fit(
    anz_schrauben: int, vol_schrauben: int, anz_scheiben: int, vol_scheiben: int, vol_container: int
) -> None:
    """
    A test example that tries to figure out if all the Schrauben and Scheiben fit in the container, and if the smallest
    possible container is chosen.

    :param anz_schrauben: Number of Schraubenpäckchen
    :param vol_schrauben: Volume (mm³) of a single Schraubenpäckchen
    :param anz_scheiben: Number of Scheibenpäckchen
    :param vol_scheiben: Volume (mm³) of a single Scheibenpäckchen
    :param vol_container: Volume (mm³) of the selected container
    """

    available_container_sizes = map(lambda x: x * 1_000_000_000, [1, 5, 10])

    size_required = (anz_schrauben * vol_schrauben) + (anz_scheiben * vol_scheiben)

    # Smallest possible Container ordered?
    smallest_possible_container = min(filter(lambda x: x >= size_required, available_container_sizes))
    assert vol_container == smallest_possible_container
