#!/usr/bin/env python3
"""
Configuration resolver - single source of truth for all config resolution

Two mutually exclusive modes:
1. CONFIG mode (--config FILE): YAML controls everything
2. DIRECT mode (no --config): CLI args > Env > Defaults

Precedence (CONFIG mode):
  - YAML (authoritative)
  - Env vars (only for secrets/credentials via EnvConfig)
  - Error if required fields missing

Precedence (DIRECT mode):
  - CLI args (explicit, Typer defaults must be None)
  - Environment variables (CHROMIUM_SRC, ARCH)
  - Hardcoded defaults

This centralizes ALL configuration resolution in one place.
"""

from pathlib import Path
from typing import Optional, List, Dict, Any, Tuple

from .context import Context
from .env import EnvConfig
from .utils import get_platform_arch, log_info


def resolve_config(
    cli_args: Dict[str, Any],
    yaml_config: Optional[Dict[str, Any]] = None,
) -> Context:
    """Resolve build configuration - single entry point.

    Args:
        cli_args: Dictionary of CLI arguments (all values should be None if not provided)
        yaml_config: Optional YAML configuration (triggers CONFIG mode)

    Returns:
        Fully resolved Context object

    Raises:
        ValueError: If required fields missing or invalid

    Modes:
        - CONFIG mode (yaml_config provided): YAML is authoritative
        - DIRECT mode (no yaml_config): CLI > Env > Defaults

    Note:
        root_dir is always computed from package location via get_package_root(),
        never from config or cwd.
    """
    if yaml_config:
        return _resolve_config_mode(yaml_config, cli_args)
    else:
        return _resolve_direct_mode(cli_args)


def _resolve_config_mode(
    yaml_config: Dict[str, Any], cli_args: Dict[str, Any]
) -> Context:
    """CONFIG MODE: YAML is base, CLI can override.

    Args:
        yaml_config: YAML configuration dictionary
        cli_args: CLI arguments (can override YAML values)

    Returns:
        Context with values from YAML, optionally overridden by CLI

    Raises:
        ValueError: If required fields missing from both YAML and CLI
    """
    build_section = yaml_config.get("build", {})

    # chromium_src: CLI override > YAML > error
    chromium_src_str = cli_args.get("chromium_src") or build_section.get("chromium_src")
    if not chromium_src_str:
        raise ValueError(
            "CONFIG MODE: chromium_src required in YAML!\n"
            "Add to your config:\n"
            "  build:\n"
            "    chromium_src: /path/to/chromium"
        )

    chromium_src = Path(chromium_src_str)
    chromium_src_source = "cli" if cli_args.get("chromium_src") else "yaml"

    # Validate chromium_src exists
    if not chromium_src.exists():
        raise ValueError(
            f"CONFIG MODE: chromium_src does not exist: {chromium_src}\n"
            f"Expected directory with Chromium source code"
        )

    # architecture: CLI override > YAML > platform default
    architecture = (
        cli_args.get("arch")
        or build_section.get("architecture")
        or build_section.get("arch")
    )
    arch_source = "cli" if cli_args.get("arch") else "yaml"
    if not architecture:
        architecture = get_platform_arch()
        arch_source = "default"
        log_info(f"CONFIG MODE: Using platform default architecture: {architecture}")

    # build_type: CLI override > YAML > debug
    build_type = cli_args.get("build_type") or build_section.get("type", "debug")
    build_type_source = "cli" if cli_args.get("build_type") else "yaml"

    log_info(f"✓ CONFIG MODE: chromium_src={chromium_src} ({chromium_src_source})")
    log_info(f"✓ CONFIG MODE: architecture={architecture} ({arch_source})")
    log_info(f"✓ CONFIG MODE: build_type={build_type} ({build_type_source})")

    return Context(
        chromium_src=chromium_src,
        architecture=architecture,
        build_type=build_type,
    )


def _resolve_direct_mode(cli_args: Dict[str, Any]) -> Context:
    """DIRECT MODE: CLI > Env > Defaults.

    Args:
        cli_args: CLI arguments (None if not provided by user)

    Returns:
        Context with resolved values

    Raises:
        ValueError: If chromium_src not provided
    """
    env = EnvConfig()

    # chromium_src: CLI > Env > Error
    chromium_src = cli_args.get("chromium_src") or env.chromium_src
    if not chromium_src:
        raise ValueError(
            "DIRECT MODE: chromium_src required!\n"
            "Provide via one of:\n"
            "  --chromium-src PATH\n"
            "  CHROMIUM_SRC environment variable"
        )

    chromium_src = Path(chromium_src)

    # Validate chromium_src exists
    if not chromium_src.exists():
        raise ValueError(
            f"DIRECT MODE: chromium_src does not exist: {chromium_src}\n"
            f"Expected directory with Chromium source code"
        )

    # architecture: CLI > Env > Platform default
    architecture = cli_args.get("arch") or env.arch
    if not architecture:
        architecture = get_platform_arch()
        log_info(f"DIRECT MODE: Using platform default architecture: {architecture}")

    # build_type: CLI > Default
    build_type = cli_args.get("build_type") or "debug"

    log_info(f"✓ DIRECT MODE: chromium_src={chromium_src} (cli/env)")
    log_info(f"✓ DIRECT MODE: architecture={architecture} (cli/env/default)")
    log_info(f"✓ DIRECT MODE: build_type={build_type} (cli/default)")

    return Context(
        chromium_src=chromium_src,
        architecture=architecture,
        build_type=build_type,
    )


def resolve_pipeline(
    cli_args: Dict[str, Any],
    yaml_config: Optional[Dict[str, Any]] = None,
    execution_order: Optional[List[Tuple[str, List[str]]]] = None,
) -> List[str]:
    """Resolve build pipeline - single entry point.

    Args:
        cli_args: CLI arguments dictionary
        yaml_config: Optional YAML configuration (triggers CONFIG mode)
        execution_order: Required for DIRECT mode with phase flags

    Returns:
        List of module names in execution order

    Raises:
        ValueError: If no pipeline specified or conflicting modes

    Modes:
        - CONFIG mode: Returns yaml_config["modules"]
        - DIRECT mode: --modules or phase flags
    """
    if yaml_config:
        return _resolve_pipeline_config_mode(yaml_config)
    else:
        return _resolve_pipeline_direct_mode(cli_args, execution_order)


def _resolve_pipeline_config_mode(yaml_config: Dict[str, Any]) -> List[str]:
    """CONFIG MODE: Pipeline from YAML modules list.

    Args:
        yaml_config: YAML configuration dictionary

    Returns:
        Module list from YAML

    Raises:
        ValueError: If modules not specified in YAML
    """
    modules = yaml_config.get("modules")
    if not modules:
        raise ValueError(
            "CONFIG MODE: modules required in YAML!\n"
            "Add to your config:\n"
            "  modules: [clean, configure, compile, sign_macos]"
        )

    log_info(f"✓ CONFIG MODE: pipeline={modules} (yaml)")
    return modules


def _resolve_pipeline_direct_mode(
    cli_args: Dict[str, Any],
    execution_order: Optional[List[Tuple[str, List[str]]]],
) -> List[str]:
    """DIRECT MODE: Pipeline from --modules or phase flags.

    Args:
        cli_args: CLI arguments dictionary
        execution_order: Phase execution order (required for flag mode)

    Returns:
        Module list in execution order

    Raises:
        ValueError: If no pipeline specified or both modes used
    """
    has_modules = cli_args.get("modules") is not None
    has_flags = _has_phase_flags(cli_args)

    if not has_modules and not has_flags:
        raise ValueError(
            "DIRECT MODE: No pipeline specified!\n"
            "Use one of:\n"
            "  --modules clean,compile,...\n"
            "  --setup --build --sign  (phase flags)"
        )

    if has_modules and has_flags:
        raise ValueError(
            "DIRECT MODE: Cannot use both --modules and phase flags!\n"
            "Choose one approach."
        )

    if has_modules:
        modules_str = cli_args["modules"]
        pipeline = [m.strip() for m in modules_str.split(",")]
        log_info(f"✓ DIRECT MODE: pipeline={pipeline} (--modules)")
        return pipeline

    if has_flags:
        if execution_order is None:
            raise ValueError(
                "DIRECT MODE: execution_order required for phase flag resolution"
            )
        pipeline = _build_pipeline_from_flags(cli_args, execution_order)
        log_info(f"✓ DIRECT MODE: pipeline={pipeline} (phase flags)")
        return pipeline

    raise ValueError("DIRECT MODE: Internal error - no pipeline resolution matched")


def _has_phase_flags(cli_args: Dict[str, Any]) -> bool:
    """Check if any phase flags are set.

    Args:
        cli_args: CLI arguments dictionary

    Returns:
        True if any phase flag is True
    """
    phase_flags = ["setup", "prep", "build", "sign", "package", "upload"]
    return any(cli_args.get(flag, False) for flag in phase_flags)


def _build_pipeline_from_flags(
    cli_args: Dict[str, Any],
    execution_order: List[Tuple[str, List[str]]],
) -> List[str]:
    """Build pipeline from phase flags with fixed execution order.

    Args:
        cli_args: CLI arguments with phase flag keys
        execution_order: List of (phase_name, modules) defining order

    Returns:
        Module list in predetermined order
    """
    enabled_phases = {
        "setup": cli_args.get("setup", False),
        "prep": cli_args.get("prep", False),
        "build": cli_args.get("build", False),
        "sign": cli_args.get("sign", False),
        "package": cli_args.get("package", False),
        "upload": cli_args.get("upload", False),
    }

    pipeline = []
    for phase_name, phase_modules in execution_order:
        if enabled_phases.get(phase_name, False):
            pipeline.extend(phase_modules)

    return pipeline
