from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Dict, Optional
import polars as pl
import xml.etree.ElementTree as ET
from engine.graph import NodeDef
from engine.context import RunContext


class BaseTool(ABC):
    def __init__(self, node: NodeDef, ctx: RunContext):
        self.node = node
        self.ctx = ctx
        self.config: Optional[ET.Element] = node.config

    @abstractmethod
    def execute(self, inputs: Dict[str, pl.DataFrame]) -> Dict[str, pl.DataFrame]:
        """Execute the tool and return named output DataFrames."""

    def _cfg(self, xpath: str, default: Optional[str] = None) -> Optional[str]:
        el = self.config.find(xpath) if self.config is not None else None
        return el.text if el is not None else default

    def _cfg_attr(self, xpath: str, attr: str, default: Optional[str] = None) -> Optional[str]:
        el = self.config.find(xpath) if self.config is not None else None
        return el.attrib.get(attr, default) if el is not None else default

    def _cfg_text(self, xpath: str, default: str = "") -> str:
        val = self._cfg(xpath, default)
        return val if val is not None else default
