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

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


class UpsertExecutor(AUniversalOperationExecutor):
    """
    UPSERT operation executor - Universal operation.
    Insert if not exists, update if exists (based on key fields).
    O(n) scan to find matching record by key_fields.
    Capability: Universal
    Operation Type: CORE
    """
    OPERATION_NAME = "UPSERT"
    OPERATION_TYPE = OperationType.CORE
    SUPPORTED_NODE_TYPES = []  # Universal

    def _do_execute(self, action: QueryAction, context: ExecutionContext) -> ExecutionResult:
        """Execute UPSERT operation."""
        params = action.params
        target = params.get('target')
        data = params.get('data')
        key_fields = params.get('key_fields', [])

        if not target:
            return ExecutionResult(
                success=False,
                data=None,
                error=f"UPSERT requires 'target'. Got: {action.params}",
                action_type=self.OPERATION_NAME
            )
        if not data or not isinstance(data, dict):
            return ExecutionResult(
                success=False,
                data=None,
                error=f"UPSERT requires 'data' as dict. Got: {type(data).__name__}",
                action_type=self.OPERATION_NAME
            )
        if not key_fields:
            return ExecutionResult(
                success=False,
                data=None,
                error="UPSERT requires 'key_fields' (list of fields to match on).",
                action_type=self.OPERATION_NAME
            )

        node = context.node
        result_data = self._execute_upsert(node, target, data, key_fields, context)

        return ExecutionResult(
            success=True,
            data=result_data,
            action_type=self.OPERATION_NAME,
            metadata={
                'target': target,
                'operation': result_data.get('operation'),
                'key_fields': key_fields
            }
        )

    def _execute_upsert(self, node: Any, target: str, data: dict,
                        key_fields: list, context: ExecutionContext) -> dict:
        """
        Actual UPSERT logic - O(n) scan for matching record.
        If a record matches all key_fields, update it in place.
        Otherwise append the new record.
        """
        try:
            # Resolve target collection
            collection = None
            if isinstance(node, dict) and target in node:
                collection = node[target]
            elif hasattr(node, 'get'):
                try:
                    collection = node.get(target, default=None)
                except Exception:
                    collection = None

            if collection is None:
                # Auto-create collection and insert
                if isinstance(node, dict):
                    node[target] = [data]
                    return {
                        'operation': 'insert',
                        'target': target,
                        'record': data,
                        'message': 'Collection created and record inserted'
                    }
                return {
                    'operation': 'insert',
                    'target': target,
                    'record': data,
                    'message': 'In-memory upsert (no persistent collection)'
                }

            items = extract_items(collection)

            # O(n) scan: find matching record by key_fields
            match_index = None
            for i, item in enumerate(items):
                if self._keys_match(item, data, key_fields):
                    match_index = i
                    break

            if match_index is not None:
                # Update: merge new data into existing record
                existing = items[match_index]
                if isinstance(existing, dict):
                    existing.update(data)
                elif hasattr(existing, '__dict__'):
                    for k, v in data.items():
                        setattr(existing, k, v)

                return {
                    'operation': 'update',
                    'target': target,
                    'record': data,
                    'matched_index': match_index,
                    'key_fields': key_fields
                }
            else:
                # Insert: append new record
                if isinstance(collection, list):
                    collection.append(data)
                elif hasattr(collection, 'append'):
                    collection.append(data)

                return {
                    'operation': 'insert',
                    'target': target,
                    'record': data,
                    'key_fields': key_fields
                }

        except Exception as e:
            return {
                'operation': 'error',
                'target': target,
                'error': str(e)
            }

    @staticmethod
    def _keys_match(item: Any, data: dict, key_fields: list) -> bool:
        """Check if all key_fields in item match the corresponding values in data."""
        for field in key_fields:
            item_val = extract_field_value(item, field)
            data_val = data.get(field)
            if item_val != data_val:
                return False
        return True


__all__ = ['UpsertExecutor']
