"""Configuration file parsing for paude."""

from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Any

from paude.config.models import FeatureSpec, PaudeConfig
from paude.config.user_config import _warn_unknown_keys


class ConfigError(Exception):
    """Error parsing configuration file."""


def parse_config(config_file: Path) -> PaudeConfig:
    """Parse a configuration file.

    Args:
        config_file: Path to the config file (devcontainer.json or paude.json).

    Returns:
        Parsed configuration.

    Raises:
        ConfigError: If the file cannot be parsed.
    """
    try:
        content = config_file.read_text()
        data = json.loads(content)
    except json.JSONDecodeError as e:
        raise ConfigError(f"Invalid JSON in {config_file}: {e}") from e
    except OSError as e:
        raise ConfigError(f"Cannot read {config_file}: {e}") from e

    # Determine config type
    if "devcontainer" in config_file.name or config_file.parent.name == ".devcontainer":
        return _parse_devcontainer(config_file, data)
    elif config_file.name == "paude.json":
        return _parse_paude_json(config_file, data)
    else:
        raise ConfigError(f"Unknown config file type: {config_file}")


def _extract_build_config(
    config_dir: Path, data: dict[str, Any]
) -> tuple[Path | None, Path | None, dict[str, str]]:
    """Extract dockerfile, build context, and build args from config data.

    Args:
        config_dir: Directory containing the config file.
        data: Parsed JSON data (must contain optional "build" key).

    Returns:
        Tuple of (dockerfile, build_context, build_args).
    """
    build_config = data.get("build", {})
    dockerfile: Path | None = None
    build_context: Path | None = None

    if "dockerfile" in build_config:
        dockerfile_path = build_config["dockerfile"]
        if not Path(dockerfile_path).is_absolute():
            dockerfile = config_dir / dockerfile_path
        else:
            dockerfile = Path(dockerfile_path)

    if "context" in build_config:
        context_path = build_config["context"]
        if not Path(context_path).is_absolute():
            build_context = config_dir / context_path
        else:
            build_context = Path(context_path)
        if build_context.exists():
            build_context = build_context.resolve()
    elif dockerfile:
        build_context = config_dir

    build_args = build_config.get("args", {})
    return dockerfile, build_context, build_args


def _parse_devcontainer(config_file: Path, data: dict[str, Any]) -> PaudeConfig:
    """Parse a devcontainer.json file.

    Args:
        config_file: Path to the config file.
        data: Parsed JSON data.

    Returns:
        Parsed configuration.
    """
    config_dir = config_file.parent

    # Extract image
    base_image = data.get("image")

    # Extract build config
    dockerfile, build_context, build_args = _extract_build_config(config_dir, data)

    # Parse features
    features: list[FeatureSpec] = []
    if "features" in data:
        for feature_url, options in data["features"].items():
            if isinstance(options, dict):
                features.append(FeatureSpec(url=feature_url, options=options))
            else:
                features.append(FeatureSpec(url=feature_url, options={}))
        if features:
            print(f"Found {len(features)} feature(s)", file=sys.stderr)

    # Parse postCreateCommand
    post_create_command: str | None = None
    if "postCreateCommand" in data:
        pcc = data["postCreateCommand"]
        if isinstance(pcc, list):
            post_create_command = " && ".join(pcc)
        else:
            post_create_command = pcc

    # Parse containerEnv
    container_env = data.get("containerEnv", {})

    # Warn about unsupported properties
    _warn_unsupported_properties(data)

    # Parse create hints from customizations.paude.create
    create_section = data.get("customizations", {}).get("paude", {}).get("create", {})
    create_allowed_domains, create_agent, create_provider, create_otel_endpoint = (
        _parse_create_section(create_section)
    )

    return PaudeConfig(
        config_file=config_file,
        config_type="devcontainer",
        base_image=base_image,
        dockerfile=dockerfile,
        build_context=build_context,
        features=features,
        post_create_command=post_create_command,
        container_env=container_env,
        build_args=build_args,
        create_allowed_domains=create_allowed_domains,
        create_agent=create_agent,
        create_provider=create_provider,
        create_otel_endpoint=create_otel_endpoint,
    )


def _parse_paude_json(config_file: Path, data: dict[str, Any]) -> PaudeConfig:
    """Parse a paude.json file.

    Args:
        config_file: Path to the config file.
        data: Parsed JSON data.

    Returns:
        Parsed configuration.
    """
    config_dir = config_file.parent

    base_image = data.get("base")
    packages = data.get("packages", [])
    setup_command = data.get("setup")

    # Extract build config
    dockerfile, build_context, build_args = _extract_build_config(config_dir, data)

    if "pip_install" in data:
        print(
            "Warning: 'pip_install' is deprecated and ignored.",
            file=sys.stderr,
        )
        print(
            "  → Use 'paude remote add --push' to sync, then install manually.",
            file=sys.stderr,
        )

    # Parse "create" section for create hints
    create_allowed_domains, create_agent, create_provider, create_otel_endpoint = (
        _parse_create_section(data.get("create", {}))
    )

    return PaudeConfig(
        config_file=config_file,
        config_type="paude",
        base_image=base_image,
        dockerfile=dockerfile,
        build_context=build_context,
        build_args=build_args,
        packages=packages,
        post_create_command=setup_command,
        create_allowed_domains=create_allowed_domains,
        create_agent=create_agent,
        create_provider=create_provider,
        create_otel_endpoint=create_otel_endpoint,
    )


_KNOWN_CREATE_KEYS = {"allowed-domains", "agent", "provider", "otel-endpoint"}


def _parse_create_section(
    create_data: dict[str, Any],
) -> tuple[list[str], str | None, str | None, str | None]:
    """Parse the 'create' section from project config.

    Args:
        create_data: The parsed "create" object (may be empty).

    Returns:
        Tuple of (allowed_domains, agent, provider, otel_endpoint).
    """
    if not isinstance(create_data, dict):
        return [], None, None, None

    _warn_unknown_keys(create_data, _KNOWN_CREATE_KEYS, "create section")

    allowed_domains = create_data.get("allowed-domains", [])
    if not isinstance(allowed_domains, list):
        allowed_domains = []

    agent = create_data.get("agent")
    if agent is not None and not isinstance(agent, str):
        agent = None

    provider = create_data.get("provider")
    if provider is not None and not isinstance(provider, str):
        provider = None

    otel_endpoint = create_data.get("otel-endpoint")
    if otel_endpoint is not None and not isinstance(otel_endpoint, str):
        otel_endpoint = None

    return allowed_domains, agent, provider, otel_endpoint


def _warn_unsupported_properties(data: dict[str, Any]) -> None:
    """Warn about unsupported properties in devcontainer.json."""
    unsupported = [
        "mounts",
        "runArgs",
        "privileged",
        "capAdd",
        "forwardPorts",
        "remoteUser",
    ]
    for prop in unsupported:
        if prop in data:
            print(
                f"Warning: Ignoring unsupported property '{prop}' in config",
                file=sys.stderr,
            )
            print("  → paude controls this for security", file=sys.stderr)
