"""
Configuration loading utilities.

Provides unified utilities for loading configuration from files,
environment variables, and other sources.
"""

import os
import json
from pathlib import Path
from typing import Dict, Any, Optional, Union

from .exceptions import ConfigFileError, EnvironmentVariableError


def load_env_file(file_path: Union[str, Path],
                  required: bool = False) -> Dict[str, str]:
    """Load environment variables from a file.

    Consolidates the duplicate load_dotenv_file() functions found in
    tddai/config.py and gitea/config.py.

    Args:
        file_path: Path to the environment file
        required: Whether the file must exist

    Returns:
        Dictionary of environment variables loaded from file

    Raises:
        ConfigFileError: If required file is missing or cannot be parsed
    """
    file_path = Path(file_path)

    if not file_path.exists():
        if required:
            raise ConfigFileError(
                f"Required configuration file not found: {file_path}",
                suggestion=f"Create {file_path} or use environment variables"
            )
        return {}

    try:
        env_vars = {}
        with file_path.open('r') as f:
            for line_num, line in enumerate(f, 1):
                line = line.strip()

                # Skip empty lines and comments
                if not line or line.startswith('#'):
                    continue

                # Parse KEY=VALUE format
                if '=' not in line:
                    continue

                key, value = line.split('=', 1)
                key = key.strip()
                value = value.strip()

                # Remove quotes if present
                if value.startswith('"') and value.endswith('"'):
                    value = value[1:-1]
                elif value.startswith("'") and value.endswith("'"):
                    value = value[1:-1]

                env_vars[key] = value

        return env_vars

    except (OSError, UnicodeDecodeError) as e:
        raise ConfigFileError(
            f"Failed to read configuration file: {file_path}",
            context={'error': str(e), 'line': line_num if 'line_num' in locals() else None}
        ) from e


def resolve_path(path: Union[str, Path],
                 base_dir: Optional[Union[str, Path]] = None,
                 create_parents: bool = False) -> Path:
    """Resolve and normalize a file path.

    Handles tilde expansion, relative paths, and optionally creates parent directories.

    Args:
        path: Path to resolve
        base_dir: Base directory for relative paths (defaults to current working directory)
        create_parents: Whether to create parent directories if they don't exist

    Returns:
        Resolved absolute Path object

    Raises:
        ConfigFileError: If path resolution fails or parent creation fails
    """
    try:
        # Convert to Path object
        path = Path(path)

        # Expand user home directory (~)
        if str(path).startswith('~'):
            path = path.expanduser()

        # Handle relative paths
        if not path.is_absolute():
            if base_dir:
                base_dir = Path(base_dir).expanduser().resolve()
                path = base_dir / path
            else:
                path = Path.cwd() / path

        # Resolve to absolute path
        path = path.resolve()

        # Create parent directories if requested
        if create_parents and not path.parent.exists():
            path.parent.mkdir(parents=True, exist_ok=True)

        return path

    except (OSError, RuntimeError) as e:
        raise ConfigFileError(
            f"Failed to resolve path: {path}",
            context={'error': str(e), 'base_dir': base_dir}
        ) from e


def get_env_var(key: str,
                default: Optional[str] = None,
                required: bool = False,
                var_type: type = str) -> Any:
    """Get environment variable with type conversion and validation.

    Args:
        key: Environment variable name
        default: Default value if not found
        required: Whether the variable is required
        var_type: Type to convert the value to (str, int, bool, Path)

    Returns:
        Environment variable value converted to specified type

    Raises:
        EnvironmentVariableError: If required variable is missing or conversion fails
    """
    value = os.getenv(key)

    if value is None:
        if required:
            raise EnvironmentVariableError(
                f"Required environment variable not set: {key}",
                field=key,
                suggestion=f"Set {key} environment variable or provide default in config file"
            )
        return default

    # Type conversion
    try:
        if var_type == bool:
            return value.lower() in ('true', '1', 'yes', 'on')
        elif var_type == int:
            return int(value)
        elif var_type == float:
            return float(value)
        elif var_type == Path:
            return resolve_path(value)
        else:
            return var_type(value)

    except (ValueError, TypeError) as e:
        raise EnvironmentVariableError(
            f"Invalid value for environment variable {key}: {value}",
            field=key,
            value=value,
            suggestion=f"Provide a valid {var_type.__name__} value for {key}"
        ) from e


def load_json_config(file_path: Union[str, Path],
                     required: bool = False) -> Dict[str, Any]:
    """Load configuration from JSON file.

    Args:
        file_path: Path to JSON configuration file
        required: Whether the file must exist

    Returns:
        Configuration dictionary loaded from JSON

    Raises:
        ConfigFileError: If file cannot be read or parsed
    """
    file_path = Path(file_path)

    if not file_path.exists():
        if required:
            raise ConfigFileError(
                f"Required JSON configuration file not found: {file_path}",
                suggestion=f"Create {file_path} with valid JSON configuration"
            )
        return {}

    try:
        with file_path.open('r') as f:
            return json.load(f)

    except json.JSONDecodeError as e:
        raise ConfigFileError(
            f"Invalid JSON in configuration file: {file_path}",
            context={'error': str(e), 'line': e.lineno, 'column': e.colno}
        ) from e

    except (OSError, UnicodeDecodeError) as e:
        raise ConfigFileError(
            f"Failed to read JSON configuration file: {file_path}",
            context={'error': str(e)}
        ) from e