"""
Multi-Config Discovery System

Provides functionality to recursively discover and manage multiple .pipeline.yaml
configuration files within an organization's billing configs directory.
"""

from dataclasses import dataclass
from pathlib import Path
from typing import (
    Any,
    Dict,
    List,
    Optional,
)

import yaml


@dataclass
class ConfigFile:
    """Represents a discovered pipeline configuration file."""

    path: Path
    relative_path: Path
    name: str
    content: Optional[Dict[str, Any]] = None

    def __str__(self) -> str:
        return str(self.relative_path)

    def load_content(self) -> Dict[str, Any]:
        """Load YAML content from the config file."""
        if self.content is None:
            with open(self.path, encoding="utf-8") as f:
                self.content = yaml.safe_load(f) or {}
        return self.content


class ConfigDiscoveryError(Exception):
    """Raised when config discovery encounters an error."""

    pass


def discover_pipeline_configs(org_dir: Path) -> List[ConfigFile]:
    """
    Recursively discover all .pipeline.yaml files in org configs directory.

    Args:
        org_dir: Organization directory path (e.g., orgs/demo_test/)

    Returns:
        List of ConfigFile objects sorted by relative path

    Raises:
        ConfigDiscoveryError: If configs directory doesn't exist or no configs found
    """
    configs_dir = org_dir / "billing" / "pipeline-configs"

    # Validate configs directory exists
    if not configs_dir.exists():
        raise ConfigDiscoveryError(
            f"Configs directory not found: {configs_dir}. "
            f"Expected directory structure: {org_dir}/billing/pipeline-configs/"
        )

    if not configs_dir.is_dir():
        raise ConfigDiscoveryError(f"Configs path is not a directory: {configs_dir}")

    # Discover all .pipeline.yaml files recursively
    config_files = list(configs_dir.rglob("*.pipeline.yaml"))

    if not config_files:
        raise ConfigDiscoveryError(
            f"No .pipeline.yaml files found in {configs_dir}. "
            f"Please create at least one config file with .pipeline.yaml extension."
        )

    # Create ConfigFile objects with metadata
    discovered_configs = []
    for config_file in sorted(config_files):
        try:
            relative_path = config_file.relative_to(configs_dir)
            # Extract base name without .pipeline.yaml extension
            config_name = config_file.name.replace(".pipeline.yaml", "")

            discovered_configs.append(
                ConfigFile(
                    path=config_file, relative_path=relative_path, name=config_name
                )
            )
        except ValueError as e:
            # This shouldn't happen with rglob, but handle gracefully
            raise ConfigDiscoveryError(
                f"Error processing config file {config_file}: {e}"
            )

    return discovered_configs


def get_config_discovery_summary(config_files: List[ConfigFile]) -> Dict[str, Any]:
    """
    Generate a summary of discovered configuration files.

    Args:
        config_files: List of discovered config files

    Returns:
        Dictionary with discovery statistics and file listing
    """
    if not config_files:
        return {
            "total_configs": 0,
            "config_files": [],
            "directories": [],
            "summary": "No configuration files discovered",
        }

    # Extract unique directories
    directories = set()
    for config_file in config_files:
        parent_dir = config_file.relative_path.parent
        if parent_dir != Path("."):  # Skip root configs directory
            directories.add(str(parent_dir))

    return {
        "total_configs": len(config_files),
        "config_files": [str(cf.relative_path) for cf in config_files],
        "directories": sorted(directories),
        "summary": f"Discovered {len(config_files)} config file(s) across {len(directories)} director{'ies' if len(directories) != 1 else 'y'}",
    }


def validate_config_file_access(config_files: List[ConfigFile]) -> None:
    """
    Validate that all discovered config files are readable.

    Args:
        config_files: List of config files to validate

    Raises:
        ConfigDiscoveryError: If any config file is not readable
    """
    unreadable_files = []

    for config_file in config_files:
        try:
            # Test file readability without loading full content
            with open(config_file.path, encoding="utf-8") as f:
                f.read(1)  # Read one character to test access
        except (OSError, UnicodeDecodeError) as e:
            unreadable_files.append((config_file.relative_path, str(e)))

    if unreadable_files:
        error_details = []
        for file_path, error in unreadable_files:
            error_details.append(f"  • {file_path}: {error}")

        raise ConfigDiscoveryError(
            f"Found {len(unreadable_files)} unreadable config file(s):\n"
            + "\n".join(error_details)
        )


def discover_and_validate_configs(
    org_dir: Path, verbose: bool = False
) -> List[ConfigFile]:
    """
    Discover and validate all pipeline configs for an organization.

    This is the main entry point that combines discovery, validation, and
    optional verbose reporting.

    Args:
        org_dir: Organization directory path
        verbose: Whether to print discovery details

    Returns:
        List of validated ConfigFile objects

    Raises:
        ConfigDiscoveryError: If discovery or validation fails
    """
    # Phase 1: Discovery
    config_files = discover_pipeline_configs(org_dir)

    # Phase 2: Access validation
    validate_config_file_access(config_files)

    # Phase 3: Summary (optional verbose output)
    if verbose:
        summary = get_config_discovery_summary(config_files)
        print("📋 Config Discovery Summary:")
        print(f"   Total configs: {summary['total_configs']}")
        print(f"   Directories: {len(summary['directories'])}")

        print("\n📁 Discovered config files:")
        for config_file in config_files:
            print(f"   • {config_file.relative_path}")

        if summary["directories"]:
            print("\n📂 Config directories:")
            for directory in summary["directories"]:
                print(f"   • {directory}/")

    return config_files


# Convenience functions for common use cases
def count_pipeline_configs(org_dir: Path) -> int:
    """
    Count the number of .pipeline.yaml files in an org's configs directory.

    Args:
        org_dir: Organization directory path

    Returns:
        Number of .pipeline.yaml files found
    """
    try:
        config_files = discover_pipeline_configs(org_dir)
        return len(config_files)
    except ConfigDiscoveryError:
        return 0


def has_pipeline_configs(org_dir: Path) -> bool:
    """
    Check if an organization has any .pipeline.yaml files.

    Args:
        org_dir: Organization directory path

    Returns:
        True if at least one .pipeline.yaml file exists
    """
    return count_pipeline_configs(org_dir) > 0
