"""Reactive configuration parser (actor-first, minimal port).

This is a lightweight adaptation of the v2 parser. It loads YAML/JSON files,
interpolates env vars minimally, and produces ReactiveConfig with RouteConfig
entries (stream/graph). Template rendering is intentionally simplified.
"""

from __future__ import annotations

import os
import re
from pathlib import Path
from typing import Any

import yaml
from pydantic import BaseModel, Field

from cleveragents.core.exceptions import ConfigurationError
from cleveragents.reactive.route import BridgeConfig, RouteConfig, RouteType
from cleveragents.reactive.stream_router import StreamType


class AgentConfig(BaseModel):
    name: str
    type: str
    config: dict[str, Any] = Field(default_factory=dict)


class LangGraphConfig(BaseModel):
    name: str
    nodes: dict[str, dict[str, Any]] = Field(default_factory=dict)
    edges: list[dict[str, Any]] = Field(default_factory=list)
    entry_point: str = "start"
    checkpointing: bool = False
    checkpoint_dir: str | None = None
    enable_time_travel: bool = False
    parallel_execution: bool = True
    state_class: str | None = None
    metadata: dict[str, Any] = Field(default_factory=dict)
    template_config: dict[str, Any] | None = None


class HybridPipelineConfig(BaseModel):
    name: str
    stages: list[dict[str, Any]] = Field(default_factory=list)
    metadata: dict[str, Any] = Field(default_factory=dict)


class ReactiveConfig(BaseModel):
    agents: dict[str, AgentConfig] = Field(default_factory=dict)
    routes: dict[str, RouteConfig] = Field(default_factory=dict)
    merges: list[dict[str, Any]] = Field(default_factory=list)
    splits: list[dict[str, Any]] = Field(default_factory=list)
    pipelines: dict[str, HybridPipelineConfig] = Field(default_factory=dict)
    templates: dict[str, dict[str, Any]] = Field(default_factory=dict)
    instances: dict[str, dict[str, Any]] = Field(default_factory=dict)
    global_context: dict[str, Any] = Field(default_factory=dict)
    template_engine: str = "JINJA2"
    prompts: dict[str, Any] = Field(default_factory=dict)


class ReactiveConfigParser:
    def __init__(self) -> None:
        self.env_pattern = re.compile(r"\${([A-Za-z0-9_]+)(?::([^}]*))?}")

    def parse_files(self, config_files: list[Path]) -> ReactiveConfig:
        combined: dict[str, Any] = {}
        for file_path in config_files:
            content = Path(file_path).read_text(encoding="utf-8")
            data = yaml.safe_load(content) or {}
            self._merge(combined, data)
        combined = self._interpolate_env(combined)
        return self._build(combined)

    def _merge(self, base: dict[str, Any], new: dict[str, Any]) -> None:
        if new is None:
            return
        for k, v in new.items():
            if k in base and isinstance(base[k], dict) and isinstance(v, dict):
                self._merge(base[k], v)
            elif k in base and isinstance(base[k], list) and isinstance(v, list):
                base[k].extend(v)
            else:
                base[k] = v

    def _interpolate_env(self, value: Any) -> Any:
        if isinstance(value, dict):
            return {k: self._interpolate_env(v) for k, v in value.items()}
        if isinstance(value, list):
            return [self._interpolate_env(v) for v in value]
        if isinstance(value, str):

            def repl(match: re.Match[str]) -> str:
                var = match.group(1)
                default = match.group(2)
                val = os.environ.get(var, default)
                if val is None:
                    raise ConfigurationError(f"Environment variable '{var}' is not set")
                return str(val)

            value = self.env_pattern.sub(repl, value)
        return value

    def _build(self, data: dict[str, Any]) -> ReactiveConfig:
        rc = ReactiveConfig()
        agents_data = data.get("agents") or data.get("actors") or {}
        for name, agent_data in (agents_data or {}).items():
            rc.agents[name] = AgentConfig(
                name=name,
                type=agent_data.get("type", "llm"),
                config=agent_data.get("config", {}),
            )

        routes_cfg = data.get("routes", {}) or {}
        if isinstance(routes_cfg, list):
            routes_cfg = {f"route_{idx}": cfg for idx, cfg in enumerate(routes_cfg)}

        for name, route_data in routes_cfg.items():
            route_type = RouteType(route_data.get("type", "stream"))
            bridge = None
            if route_data.get("bridge"):
                b = route_data["bridge"]
                bridge = BridgeConfig(
                    upgrade_conditions=b.get("upgrade_conditions", {}),
                    downgrade_conditions=b.get("downgrade_conditions", {}),
                    state_extractor=b.get("state_extractor"),
                    state_flattener=b.get("state_flattener"),
                    preserve_subscriptions=b.get("preserve_subscriptions", True),
                    preserve_checkpointing=b.get("preserve_checkpointing", True),
                )
            if route_type == RouteType.STREAM:
                rc.routes[name] = RouteConfig(
                    name=name,
                    type=RouteType.STREAM,
                    stream_type=StreamType(route_data.get("stream_type", "cold")),
                    operators=route_data.get("operators", []),
                    subscriptions=route_data.get("subscriptions", []),
                    publications=route_data.get("publications", []),
                    agents=route_data.get("agents", []),
                    initial_value=route_data.get("initial_value"),
                    buffer_size=route_data.get("buffer_size", 1),
                    template_config=route_data.get("template_config"),
                    bridge=bridge,
                    metadata=route_data.get("metadata", {}),
                )
            else:
                rc.routes[name] = RouteConfig(
                    name=name,
                    type=RouteType.GRAPH,
                    nodes=route_data.get("nodes", {}),
                    edges=route_data.get("edges", []),
                    entry_point=route_data.get("entry_point", "start"),
                    checkpointing=route_data.get("checkpointing", False),
                    checkpoint_dir=route_data.get("checkpoint_dir"),
                    enable_time_travel=route_data.get("enable_time_travel", False),
                    parallel_execution=route_data.get("parallel_execution", True),
                    state_class=route_data.get("state_class"),
                    metadata=route_data.get("metadata", {}),
                    bridge=bridge,
                )

        rc.merges = data.get("merges", []) or []
        rc.splits = data.get("splits", []) or []
        rc.pipelines = {
            name: HybridPipelineConfig(
                name=name,
                stages=cfg.get("stages", []),
                metadata=cfg.get("metadata", {}),
            )
            for name, cfg in (data.get("pipelines", {}) or {}).items()
        }
        rc.templates = data.get("templates", {}) or {}
        rc.instances = data.get("instances", {}) or {}
        rc.global_context = data.get("global_context", {}) or {}
        rc.template_engine = data.get("template_engine", rc.template_engine)
        rc.prompts = data.get("prompts", {}) or {}
        return rc
