#!/usr/bin/env python3
"""
REGEX Executor
Company: eXonware.com
Author: eXonware Backend Team
Email: connect@exonware.com
Version: 0.9.0.6
"""

import re
from typing import Any, Optional
from ..base import AUniversalOperationExecutor
from ....contracts import QueryAction, ExecutionContext, ExecutionResult
from ....defs import OperationType
from ..utils import extract_items, extract_field_value


class RegexExecutor(AUniversalOperationExecutor):
    """Filter items where a regex pattern matches a field value. O(n)."""

    OPERATION_NAME = "REGEX"
    OPERATION_TYPE = OperationType.FILTERING
    SUPPORTED_NODE_TYPES = []

    _PATTERN_CACHE: dict[tuple[str, int], Any] = {}

    def _do_execute(self, action: QueryAction, context: ExecutionContext) -> ExecutionResult:
        params = action.params
        field = params.get("field")
        pattern = params.get("pattern")
        flags = params.get("flags", "")
        negate = params.get("negate", False)
        path = params.get("path", None)

        if not field:
            return ExecutionResult(
                success=False,
                data=None,
                error=f"REGEX requires 'field'. Got: {action.params}",
                action_type=self.OPERATION_NAME,
            )

        if not pattern:
            return ExecutionResult(
                success=False,
                data=None,
                error=f"REGEX requires 'pattern'. Got: {action.params}",
                action_type=self.OPERATION_NAME,
            )

        try:
            result_data = self._execute_regex(context.node, field, pattern, flags, negate, path)
        except re.error as exc:
            return ExecutionResult(
                success=False,
                data=None,
                error=f"Invalid regex pattern '{pattern}': {exc}",
                action_type=self.OPERATION_NAME,
            )

        return ExecutionResult(
            success=True,
            data=result_data,
            action_type=self.OPERATION_NAME,
            metadata={"matched_count": len(result_data.get("items", []))},
        )

    @classmethod
    def _parse_flags(cls, flags: Any) -> int:
        if not flags:
            return 0
        if isinstance(flags, int):
            return flags
        parsed = 0
        for flag in str(flags):
            if flag == "i":
                parsed |= re.IGNORECASE
            elif flag == "m":
                parsed |= re.MULTILINE
            elif flag == "s":
                parsed |= re.DOTALL
            elif flag == "x":
                parsed |= re.VERBOSE
            elif flag == "a":
                parsed |= re.ASCII
        return parsed

    @classmethod
    def _get_compiled_pattern(cls, pattern: str, regex_flags: int) -> Any:
        cache_key = (pattern, regex_flags)
        compiled = cls._PATTERN_CACHE.get(cache_key)
        if compiled is None:
            compiled = re.compile(pattern, regex_flags)
            cls._PATTERN_CACHE[cache_key] = compiled
        return compiled

    def _execute_regex(
        self,
        node: Any,
        field: str,
        pattern: str,
        flags: Any,
        negate: bool,
        path: Optional[str],
    ) -> dict:
        if path:
            try:
                data = node.get(path, default=None)
            except Exception:
                data = None
        else:
            data = node

        items = extract_items(data)
        regex_flags = self._parse_flags(flags)
        compiled = self._get_compiled_pattern(pattern, regex_flags)

        matched_items = []
        for item in items:
            field_value = extract_field_value(item, field)
            if field_value is None:
                is_match = False
            else:
                is_match = compiled.search(str(field_value)) is not None
            if negate:
                is_match = not is_match
            if is_match:
                matched_items.append(item)

        return {
            "items": matched_items,
            "count": len(matched_items),
            "total_items": len(items),
            "field": field,
            "pattern": pattern,
            "flags": flags,
            "negate": bool(negate),
        }


__all__ = ["RegexExecutor"]
