"""Built-in parser primitives for task command output parsing."""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any, Optional

from hil_testbench.data_processing.accumulators import JsonAccumulator
from hil_testbench.data_structs.parameters import ParametersSchema
from hil_testbench.task.specs import Parser


@dataclass
class JsonStreamParser(Parser):
    """Parses one JSON object per (possibly multi-line) chunk.

    Works for tools that emit brace-balanced objects or ndjson when
    configured accordingly.
    """

    ndjson: bool = False

    def __post_init__(self) -> None:
        self._acc = JsonAccumulator(ndjson=self.ndjson)

    def feed(
        self, line: str, is_error: bool, context: dict[str, Any] | None = None
    ) -> list[dict[str, Any]]:
        if is_error:
            return []
        return self._acc.feed(line)


@dataclass
class DelimitedParser(Parser):
    """Simple CSV/TSV parser mapping fields by index or header.

    If headers are provided, map by name; otherwise use provided fieldnames.
    """

    delimiter: str = ","
    fieldnames: list[str] | None = None
    has_header: bool = False

    def __post_init__(self) -> None:
        self._header: list[str] | None = None

    def feed(
        self, line: str, is_error: bool, context: dict[str, Any] | None = None
    ) -> list[dict[str, Any]]:
        if is_error:
            return []
        text = line.strip()
        if not text:
            return []
        parts = text.split(self.delimiter)

        # Establish header if present
        if self.has_header and self._header is None:
            self._header = [p.strip() for p in parts]
            return []

        # Resolve keys
        keys: list[str]
        if self._header is not None:
            keys = self._header
        elif self.fieldnames is not None:
            keys = self.fieldnames
        else:
            keys = [f"col{i}" for i in range(len(parts))]

        # Build record
        rec: dict[str, Any] = {}
        for i, k in enumerate(keys):
            rec[k] = parts[i] if i < len(parts) else None
        return [rec]


class BaseParser(ABC):
    """Base parser supporting schema validation for output fields."""

    def __init__(self, schema: Optional[ParametersSchema] = None):
        self.schema = schema

    def validate(self, event: dict):
        """Validate event dict against schema fields."""
        if self.schema:
            for field in event.keys():
                if field not in [f.name for f in self.schema.fields]:
                    raise ValueError(f"Unknown field '{field}' not in schema.")
        return True

    @abstractmethod
    def feed(self, line: str, is_error: bool):
        """Parse a line and validate output against schema. Must be implemented by subclasses."""
        pass
