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

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, parse_boolean, parse_date_iso, parse_number


class TryCastExecutor(AUniversalOperationExecutor):
    OPERATION_NAME = "TRY_CAST"
    OPERATION_TYPE = OperationType.ADVANCED
    SUPPORTED_NODE_TYPES = []

    TYPE_CONVERTERS = {
        "str": str,
    }

    def _do_execute(self, action: QueryAction, context: ExecutionContext) -> ExecutionResult:
        params = action.params
        result_data = self._execute_op(context.node, params, context)
        return ExecutionResult(
            success=True,
            data=result_data,
            action_type=self.OPERATION_NAME,
            metadata={"operation": self.OPERATION_NAME},
        )

    def _execute_op(self, node: Any, params: dict, context: ExecutionContext) -> dict:
        items = extract_items(node)
        field = params.get("field")
        target_type = str(params.get("target_type", "str")).lower()
        default = params.get("default", None)
        as_field = params.get("as_field", "_try_cast")

        result_items = []
        for item in items:
            item_copy = item.copy() if isinstance(item, dict) else {"value": item}
            source_value = extract_field_value(item, field) if field else item
            converted = self._try_cast_value(source_value, target_type)
            item_copy[as_field] = default if converted is None else converted
            result_items.append(item_copy)

        return {
            "items": result_items,
            "count": len(result_items),
            "total_items": len(items),
        }

    def _try_cast_value(self, value: Any, target_type: str) -> Optional[Any]:
        if value is None:
            return None

        try:
            if target_type == "int":
                return parse_number(value, "int")
            if target_type == "float":
                return parse_number(value, "float")
            if target_type in {"bool", "boolean"}:
                return parse_boolean(value, strict=True)
            if target_type in {"date", "datetime"}:
                return parse_date_iso(value)
            if target_type in self.TYPE_CONVERTERS:
                return self.TYPE_CONVERTERS[target_type](value)
            if target_type == "list":
                if isinstance(value, list):
                    return value
                if isinstance(value, tuple):
                    return list(value)
                return [value]
            if target_type == "dict":
                if isinstance(value, dict):
                    return value
                if isinstance(value, (list, tuple)):
                    return dict(value)
                return dict(value)
        except (TypeError, ValueError):
            return None
        return None


__all__ = ["TryCastExecutor"]
