# Copyright (C) 2023-2025 Cognizant Digital Business, Evolutionary AI.
# All Rights Reserved.
# Issued under the Academic Public License.
#
# You can be released from the terms, and requirements of the Academic Public
# License by purchasing a commercial license.
# Purchase of a commercial license is mandatory for any use of the
# neuro-san SDK Software in commercial settings.
#
# END COPYRIGHT

from typing import List

from unittest import TestCase

from neuro_san.internals.utils.file_of_class import FileOfClass
from neuro_san.test.driver.data_driven_agent_test_driver import DataDrivenAgentTestDriver
from neuro_san.test.unittest.unit_test_assert_forwarder import UnitTestAssertForwarder


class DynamicHoconUnitTests:
    """
    Assists in putting together dynamic test cases from a list of hocon files
    that describe individual test cases digestible by DataDrivenAgentTestDriver.

    Usually a single instance is created as a constant within a TestCase class and
    the parameterized.expand() annotation is used to dynamically create individual
    test methods from list of hocon files provided to it. Since the hocon files themselves
    completelt define the test cases, their names are expected to confer information as to
    just what they are testing.

    Instance methods here help run the TestCase methods.
    The main entry point here is one_test_hocon().

    Static methods are used in leveraging the paramaterized.expand annotation
    for parameterizing unit tests. The main entry point for that is from_hocon_list().

    See test_hocons.py for an example for how to use this effectively.
    """

    def __init__(self, source_file: str, path_to_basis: str = None):
        """
        Constructor

        :param source_file: The location of the source file of the TestCase.
        :param path_to_basis: The path from the source_file to the common root
                        directory of all the hocon test cases.
        """
        self.fixture_basis = FileOfClass(source_file, path_to_basis=path_to_basis)

    def one_test_hocon(self, test_case: TestCase, test_name: str, test_hocon: str):
        """
        Entry point for parameterized test methods.
        Uses the DataDrivenAgentTestDriver to effectuate the test.

        :param test_case: The instance of the TestCase class that will house
                    the individual dynamically created test methods for each hocon file.
                    This is used as a basis for reporting asserts within the framework
                    so they are properly reported within test running infrastructure.
        :param test_name: The name of the test method which was generated by the
                    @parameterize.expand annotation.
        :param test_hocon: The hocon file to use as a data-driven test case.
                    This is expanded to use the source_file/path_to_basis information
                    passed into the constructor.
        """
        # We get the test name, but don't really use it.
        # We require it as an argument because the @parameterized.expand() annotation
        # requires the test method that it is attached to take the test name as its first argument.
        # It's an artifact of the system we are leveraging. :shrug:
        _ = test_name

        # Find a full path to the test hocon file
        test_hocon_file: str = self.fixture_basis.get_file_in_basis(test_hocon)

        # Set up the driver
        asserts = UnitTestAssertForwarder(test_case)
        driver = DataDrivenAgentTestDriver(asserts)

        # Run the test
        driver.one_test(test_hocon_file)

    @staticmethod
    def from_hocon_list(hocon_files: List[str]) -> List[List[str]]:
        """
        Main entry point intended to be used inside a @parameterized.expand annotation
        which will enumerate specific test methods based on the data generated by this
        method.

        :param hocon_files: A list of hocon files to use as test cases.
                    These are expected to exist under the path implied by
                    source_file and path_to_basis passed to the constructor.
                    Individual test names are also created from each entry in the list,
                    so it behooves the class using this to not have too deep a structure
                    or use absolute paths as input here so as not to have test method
                    names that are too unwieldy.
        :return: A list intended to be passed to an @parameterized.expand annotation that will
                spell out what that needs to create individual test methods.
        """
        expansions: List[List[str]] = []

        # Go through the list of hocon files passed in to create a single entry for
        # each test case that @parameterized.expand can digest.
        for hocon_file in hocon_files:
            one_expansion: List[str] = DynamicHoconUnitTests.parameterized_expand_entry_from_hocon(hocon_file)
            expansions.append(one_expansion)
        return expansions

    @staticmethod
    def parameterized_expand_entry_from_hocon(hocon_file: str) -> List[str]:
        """
        :param hocon_file: A single reference to a hocon file used as a test case.
        :return: A List of strings that corresponds to an entry that the
                @parameterized.expand annotation can digest to create a single test method
                as part of a greater list of these.
        """
        # Each entry consists of:
        #   *   The test name to use
        #   *   The reference to the hocon file for the test case.
        return [DynamicHoconUnitTests.test_name_from_hocon_file(hocon_file), hocon_file]

    @staticmethod
    def test_name_from_hocon_file(hocon_file: str) -> str:
        """
        :param hocon_file: A hocon file reference that will be used as a test case
        :return: A reasonable name for the test method given the file path to the hocon.
                This implies that the path listed for the hocon file in @parameterized.expand
                should be descriptive enough to tell what the test is doing.
        """
        test_name: str = hocon_file.removesuffix(".hocon")
        test_name = test_name.replace("/", "_")
        return test_name
