"""
Data model of a Fortran module
"""

from __future__ import annotations

from attrs import define, field

from fgen.data_models.fortran_derived_type import FortranDerivedType
from fgen.data_models.module_requirement import ModuleRequirement


@define
class Module:
    """
    Data model of a Fortran module

    It is assumed that each module defines a single derived type.
    """

    name: str
    """Name of the module"""

    description: str
    """Description of the module"""

    provides: FortranDerivedType
    """Derived type that the module defines"""

    truncated_name: str = field()
    """
    If provided, the name to use when creating the Fortran wrapper module.

    If not supplied, we simply use the value of :attr:`name`.

    This can be used to avoid line length limits for the wrappers
    when we are wrapping modules with very long names.
    """

    requirements: list[ModuleRequirement] = field(factory=list)
    """
    Requirements from other modules

    Put another way, the modules that this module requires in order to run.
    """

    @truncated_name.default
    def truncated_name_default(self) -> str:
        """
        Default value for :attr:`truncated_name`
        """  # noqa: D401
        return self.name

    @property
    def wrapper_module_name(self) -> str:
        """
        Name for the Fortran wrapper module

        By default the wrapper module name is derived from the :attr:`name`,
        but, in some cases, the complete module name can be too long,
        leading to the wrapper module not being able to be built.
        In this case a :attr:`truncated_name` can be used
        to derive a shortened wrapper module name.
        Given that this wrapper module is only used by autogenerated code the
        readability of the name isn't as important as the full module.
        """
        return f"{self.truncated_name}_w"

    @property
    def manager_module_name(self) -> str:
        """
        Name for the Fortran manager module

        By default the manager module name is derived from the :attr:`name`,
        but, in some cases, the complete module name can be too long,
        leading to the manager module not being able to be built.
        In this case a :attr:`truncated_name` can be used
        to derive a shortened manager module name.
        Given that this manager module is only used by autogenerated code the
        readability of the name isn't as important as the full module.
        """
        return f"{self.truncated_name}_manager"

    def get_requirement_that_provides(self, provides: str) -> ModuleRequirement:
        """
        Get the module requirement that provides the specified type

        Parameters
        ----------
        provides
            Name of the provided type

        Returns
        -------
            Module requirement that provides this type

        Raises
        ------
        ValueError
            No module requirement is found that provides ``provides``
            is part of ``self.requirements``.
        """
        for requirement in self.requirements:
            if requirement.provides == provides:
                return requirement

        msg = f"No requirement provides {provides}. {self.requirements=}"
        raise ValueError(msg)
