"""
Context information for config errors.

This module defines the :class:`~relationalai.config.errors.exceptions.ConfigErrorContext` class,
which is used to track where a configuration error occurred (for example, which config file,
profile, connection, and section). This context is used to format user-facing error messages
that help users understand where the problem is in their configuration.
"""

from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from pathlib import Path

from ...util.docutils import include_in_docs

# include this module in documentation
__include_in_docs__ = True


class ContextKey(str, Enum):
    """Keys for context dictionary returned by to_dict()."""
    FILE = "file"
    SOURCE = "source"
    PROFILE = "profile"
    CONNECTION = "connection"
    SECTION = "section"


@include_in_docs
class ConfigSourceType(str, Enum):
    """Identify where configuration data was loaded from.

    This is the single source of truth for config source labels used in
    :class:`~relationalai.config.errors.context.ConfigErrorContext`.
    """
    RAI = "rai"  # raiconfig.yaml
    RAICONFIG_TOML = "raiconfig_toml"  # deprecated raiconfig.toml
    SNOWFLAKE = "snowflake"  # ~/.snowflake/config.toml
    DBT = "dbt"  # ~/.dbt/profiles.yml
    PROGRAMMATIC = "programmatic"  # Config(**data)


@dataclass
@include_in_docs
class ConfigErrorContext:
    """Track where a configuration error occurred.

    This context is used to format user-facing messages like ``Config file:
    raiconfig.yaml, Profile: dev, Connection: snowflake``.

    Attributes
    ----------
    source_type : ConfigSourceType
        Where the configuration was loaded from.
    file_path : :class:`~pathlib.Path`, optional
        Path to the config file (omitted for programmatic config).
    profile_name : str, optional
        Profile name when applicable.
    connection_name : str, optional
        Connection name when applicable.
    section : str, optional
        Configuration section name (for example, ``"connections"``).
    field_path : list[str], optional
        Nested field path within the section.
    """

    # Source information
    source_type: ConfigSourceType
    file_path: Path | None = None

    # Hierarchical context
    profile_name: str | None = None
    connection_name: str | None = None
    section: str | None = None  # e.g., "engine", "data", "compiler"

    # Field path (for nested errors)
    field_path: list[str] | None = None

    def to_dict(self) -> dict[ContextKey, str]:
        """
        Convert context to dictionary with only populated fields.

        Returns:
            Dictionary with ContextKey enum keys and string values
        """
        parts = {}

        if self.file_path:
            parts[ContextKey.FILE] = str(self.file_path)
        elif self.source_type == ConfigSourceType.PROGRAMMATIC:
            parts[ContextKey.SOURCE] = "programmatic"

        if self.profile_name:
            parts[ContextKey.PROFILE] = self.profile_name

        if self.connection_name:
            parts[ContextKey.CONNECTION] = self.connection_name

        if self.section:
            parts[ContextKey.SECTION] = self.section

        return parts

    def format_location(self) -> str:
        """
        Format context as human-readable location string.

        Returns:
            String like "Config file: raiconfig.yaml, Profile: dev, Connection: snowflake"
        """
        parts = self.to_dict()

        if not parts:
            return "Programmatic config"

        formatted = []
        if ContextKey.FILE in parts:
            formatted.append(f"Config file: {parts[ContextKey.FILE]}")
        elif ContextKey.SOURCE in parts:
            formatted.append(f"Source: {parts[ContextKey.SOURCE]}")

        if ContextKey.PROFILE in parts:
            formatted.append(f"Profile: {parts[ContextKey.PROFILE]}")

        if ContextKey.CONNECTION in parts:
            formatted.append(f"Connection: {parts[ContextKey.CONNECTION]}")

        if ContextKey.SECTION in parts:
            formatted.append(f"Section: {parts[ContextKey.SECTION]}")

        return ", ".join(formatted)
