"""Mixin class containing visitor methods associated with sifting-pattern definitions."""

__all__ = ["VisitorMixinSiftingPatterns"]

from typing import Any

from arpeggio import NonTerminal, PTNodeVisitor

from viv_compiler import errors, external_types, internal_types, utils, validation


class VisitorMixinSiftingPatterns(PTNodeVisitor):
    """A visitor mixin for Viv sifting patterns."""

    @staticmethod
    def visit_sifting_pattern(
        node: NonTerminal, children: list[Any]
    ) -> internal_types.IntermediateSiftingPatternDefinition:
        """Visit a <sifting_pattern> node."""
        # Collect all role definitions, which may be distributed across the 'roles' and 'actions' sections
        sifting_pattern_name, sifting_pattern_body = children
        all_role_definitions = []
        if "roles" in sifting_pattern_body:
            all_role_definitions += sifting_pattern_body["roles"]
        if "actions" in sifting_pattern_body:
            all_role_definitions += sifting_pattern_body["actions"]
        else:
            error_message = f"Sifting pattern '{sifting_pattern_name}' is missing 'actions' field (required)"
            raise errors.VivCompileError(error_message, source=utils.derive_source_annotations(node=node))
        validation.prevalidate_role_names(
            construct_type=external_types.ConstructDiscriminator.SIFTING_PATTERN,
            construct_name=sifting_pattern_name,
            role_definitions=all_role_definitions,
            source=utils.derive_source_annotations(node=node)
        )
        # Populate the fields that were authored
        accumulated_fields = {
            "name": sifting_pattern_name,
            "roles": {role["name"]: role for role in all_role_definitions}
        }
        for field_name, field_value in sifting_pattern_body.items():
            if field_name == "roles":
                continue  # Already handled just above
            if field_name == "actions":
                accumulated_fields["actions"] = [action['name'] for action in field_value]
            else:
                accumulated_fields[field_name] = field_value
        # Package up the intermediate definition
        intermediate_sifting_pattern_definition = internal_types.IntermediateSiftingPatternDefinition(
            type=external_types.ConstructDiscriminator.SIFTING_PATTERN,
            name=sifting_pattern_name,
            roles=accumulated_fields["roles"],
            actions=accumulated_fields["actions"],
            _conditions_raw=accumulated_fields.get("_conditions_raw", []),
        )
        return intermediate_sifting_pattern_definition

    @staticmethod
    def visit_sifting_pattern_header(
        _, children: list[external_types.SiftingPatternName]
    ) -> external_types.SiftingPatternName:
        """Visit a <sifting_pattern_header> node."""
        return children[0]

    @staticmethod
    def visit_sifting_pattern_body(_, children: list[Any]) -> dict[str, Any]:
        """Visit a <sifting_pattern_body> node."""
        accumulated_fields = {}
        for child in children:
            accumulated_fields.update(child)
        return accumulated_fields

    @staticmethod
    def visit_sifting_pattern_roles(
        _, children: list[external_types.RoleDefinition]
    ) -> dict[str, list[external_types.RoleDefinition]]:
        """Visit a <sifting_pattern_roles> node."""
        return {"roles": children}

    @staticmethod
    def visit_sifting_pattern_actions(
        _, children: list[external_types.RoleDefinition]
    ) -> dict[str, list[external_types.RoleDefinition]]:
        """Visit a <sifting_pattern_actions> node."""
        return {"actions": children}

    @staticmethod
    def visit_sifting_pattern_action(_, children: list[Any]) -> external_types.RoleDefinition:
        """Visit a <sifting_pattern_action> node."""
        # The 'actions' field in a sifting pattern is just syntactic sugar for defining
        # an action role that will also be exposed as one of the sifted actions.
        _sigil, action_name, casting_pool_directive = children
        role_definition = external_types.RoleDefinition(
            name=action_name,
            entityType=external_types.RoleEntityType.ACTION,
            min=1,
            max=1,
            parent=None,
            children=[],
            pool=casting_pool_directive["pool"],
            chance=None,
            mean=None,
            sd=None,
            participationMode=None,
            anywhere=True,
            precast=False,
            spawn=False,
            spawnFunction=None,
            renames=None
        )
        return role_definition

    @staticmethod
    def visit_sifting_pattern_conditions(
        _, children: list[list[external_types.Expression]]
    ) -> dict[str, list[external_types.Expression]]:
        """Visit a <sifting_pattern_conditions> node."""
        return {"_conditions_raw": children[0]}
