"""Configuration loading with YAML and Jinja2 support."""

from __future__ import annotations

import os
from typing import TYPE_CHECKING, Any

import yaml
from jinja2 import Environment, FileSystemLoader, StrictUndefined

from curadise_agent.config.schema import AgentConfig
from curadise_agent.config.settings import Settings
from curadise_agent.errors import ConfigurationError

if TYPE_CHECKING:
    from pathlib import Path


def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
    """Deep merge two dictionaries, with override taking precedence."""
    result = base.copy()
    for key, value in override.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = _deep_merge(result[key], value)
        else:
            result[key] = value
    return result


def _render_template(content: str, template_dir: Path) -> str:
    """Render Jinja2 template with environment variables."""
    env = Environment(
        loader=FileSystemLoader(str(template_dir)),
        undefined=StrictUndefined,
        autoescape=False,
    )
    env.globals["env"] = os.environ.get
    env.globals["environ"] = os.environ

    template = env.from_string(content)
    return template.render()


def load_yaml_file(path: Path) -> dict[str, Any]:
    """Load and parse a YAML file with Jinja2 templating."""
    if not path.exists():
        raise ConfigurationError(f"Configuration file not found: {path}")

    try:
        content = path.read_text(encoding="utf-8")
        rendered = _render_template(content, path.parent)
        data = yaml.safe_load(rendered)

        if data is None:
            return {}
        if not isinstance(data, dict):
            raise ConfigurationError(f"Configuration file must contain a mapping: {path}")

        return data
    except yaml.YAMLError as e:
        raise ConfigurationError(f"Invalid YAML in {path}: {e}", cause=e) from e
    except Exception as e:
        if isinstance(e, ConfigurationError):
            raise
        raise ConfigurationError(f"Failed to load configuration from {path}: {e}", cause=e) from e


def load_config(
    config_path: Path | None = None,
    settings: Settings | None = None,
    overrides: dict[str, Any] | None = None,
) -> AgentConfig:
    """
    Load agent configuration from multiple sources.

    Priority (highest to lowest):
    1. Explicit overrides parameter
    2. Environment variables (via Settings)
    3. Configuration file
    4. Default values

    Args:
        config_path: Path to YAML configuration file
        settings: Settings instance (created from env if None)
        overrides: Explicit overrides dict

    Returns:
        Validated AgentConfig instance
    """
    if settings is None:
        settings = Settings()

    config_data: dict[str, Any] = {}

    # Load from file if provided
    if config_path is not None:
        config_data = load_yaml_file(config_path)

    # Apply environment overrides
    env_overrides = settings.to_config_overrides()
    if env_overrides:
        config_data = _deep_merge(config_data, env_overrides)

    # Apply explicit overrides
    if overrides:
        config_data = _deep_merge(config_data, overrides)

    # Validate and return
    try:
        return AgentConfig.model_validate(config_data)
    except Exception as e:
        raise ConfigurationError(f"Invalid configuration: {e}", cause=e) from e


def validate_config_file(path: Path) -> tuple[bool, list[str]]:
    """
    Validate a configuration file without loading it fully.

    Returns:
        Tuple of (is_valid, list of error messages)
    """
    errors: list[str] = []

    try:
        load_config(config_path=path)
        return True, []
    except ConfigurationError as e:
        errors.append(str(e.message))
        if e.__cause__:
            errors.append(f"  Caused by: {e.__cause__}")
        return False, errors
    except Exception as e:
        errors.append(f"Unexpected error: {e}")
        return False, errors
