"""Action conflict resolution for Tru-Guard."""

from typing import Any

from trusys.exceptions import ActionConflictError
from trusys.types import ActionType, ConflictStrategy

# Default priority order (higher index = higher priority)
# IGNORE < FIX < MASK < ENCRYPT < FILTER < FAIL
ACTION_PRIORITY: list[ActionType] = [
    ActionType.IGNORE,  # Lowest - just log
    ActionType.FIX,  # Apply scanner's suggested_fix
    ActionType.MASK,  # Replace with ****
    ActionType.ENCRYPT,  # Format-preserving encryption
    ActionType.FILTER,  # Remove content
    ActionType.FAIL,  # Highest - raise exception
]


class ActionResolver:
    """Resolves conflicts when multiple scanners return different actions."""

    def __init__(
        self,
        strategy: ConflictStrategy = ConflictStrategy.PRIORITY,
        custom_priority: list[ActionType] | None = None,
    ) -> None:
        """Initialize the resolver.

        Args:
            strategy: Strategy for resolving conflicts
            custom_priority: Custom priority order (lowest to highest)
        """
        self.strategy = strategy
        self.priority = custom_priority or ACTION_PRIORITY

    def resolve(
        self,
        actions: list[tuple[str, ActionType, Any]],
    ) -> list[tuple[str, ActionType, Any]]:
        """Resolve multiple actions to a list of actions to apply.

        For PRIORITY strategy:
        - If any scanner's action is FAIL, returns a single-element list (FAIL wins and will raise).
        - Otherwise returns all non-IGNORE actions so each can be applied in sequence
          (each failed scanner's FIX/FILTER is applied).

        For FIRST_WINS and RAISE_ERROR strategies, returns a single-element list.

        Args:
            actions: List of (scanner_name, action_type, action_data) tuples

        Returns:
            List of (scanner_name, action_type, action_data) tuples to apply in order.
            Empty if all actions are IGNORE.

        Raises:
            ActionConflictError: If strategy is RAISE_ERROR and conflicts exist
        """
        if not actions:
            return []

        if len(actions) == 1:
            return [actions[0]]

        # Filter out IGNORE actions for conflict detection
        # (IGNORE should not cause conflicts)
        non_ignore_actions = [a for a in actions if a[1] != ActionType.IGNORE]

        if not non_ignore_actions:
            # All actions are IGNORE
            return [actions[0]]

        if len(non_ignore_actions) == 1:
            # Only one non-IGNORE action
            return [non_ignore_actions[0]]

        if self.strategy == ConflictStrategy.FIRST_WINS:
            return [non_ignore_actions[0]]

        if self.strategy == ConflictStrategy.RAISE_ERROR:
            # Check if there are actually different action types
            action_types = set(a[1] for a in non_ignore_actions)
            if len(action_types) > 1:
                conflict_info = [f"{a[0]}:{a[1].value}" for a in non_ignore_actions]
                raise ActionConflictError(conflict_info)
            # All same type, just pick first
            return [non_ignore_actions[0]]

        # PRIORITY strategy
        # If FAIL is present, it wins: return single action (runner will raise)
        fail_actions = [a for a in non_ignore_actions if a[1] == ActionType.FAIL]
        if fail_actions:
            return [max(fail_actions, key=lambda a: self._get_priority(a[1]))]

        # No FAIL: return all non-IGNORE actions to apply in sequence.
        # Sort by priority ascending (FIX first, then FILTER) so fixes then filters are applied.
        return sorted(
            non_ignore_actions,
            key=lambda a: self._get_priority(a[1]),
        )

    def _get_priority(self, action_type: ActionType) -> int:
        """Get the priority index for an action type.

        Args:
            action_type: The action type

        Returns:
            Priority index (higher = higher priority)
        """
        try:
            return self.priority.index(action_type)
        except ValueError:
            # Unknown action type gets lowest priority
            return -1

    def resolve_fixes(
        self,
        fixes: list[tuple[str, str]],
        original_content: str,
        merge_strategy: str = "chain",
    ) -> str:
        """Resolve multiple fix suggestions.

        Args:
            fixes: List of (scanner_name, suggested_fix) tuples
            original_content: The original content
            merge_strategy: How to merge fixes ("chain", "first", "longest")

        Returns:
            Resolved fix content
        """
        if not fixes:
            return original_content

        if len(fixes) == 1:
            return fixes[0][1]

        if merge_strategy == "first":
            return fixes[0][1]

        if merge_strategy == "longest":
            # Return the fix that differs most from original
            # (assuming more changes = more thorough fix)
            return max(fixes, key=lambda f: abs(len(f[1]) - len(original_content)))[1]

        # "chain" strategy - apply fixes sequentially
        # This is a simple implementation; more sophisticated
        # merging would require tracking changes
        result = original_content
        for _, fix in fixes:
            if fix:
                result = fix
        return result
