"""User-perspective tests for ``mgf.common.cli``.

Surfaces exercised:
  - Exit-code constants (numeric pin per AP-04)
  - :func:`run_and_translate` exception → exit-code translator
  - :func:`register_cli_subcommand` (long form) + :func:`subcommand`
    (short alias from v0.4.1)
  - :func:`build_cli` for consumer top-level CLIs
  - :func:`field_provenance` + :func:`env_vars_known_to_mgf`
    (promoted to public in v0.4 Phase A)
"""

from __future__ import annotations

from mgf.common.cli import (
    EXIT_CONFIG_ERROR,
    EXIT_OPERATION_ERROR,
    EXIT_PROVIDER_ERROR,
    EXIT_SUCCESS,
    EXIT_UNKNOWN,
    build_cli,
    dispatch,
    env_vars_known_to_mgf,
    field_provenance,
    register_cli_subcommand,
    run_and_translate,
    subcommand,
    unregister_cli_subcommand,
)
from mgf.common.exceptions import AppConfigError, OperationError, ResourceNotFoundError


class TestExitCodes:
    def test_numeric_values_pinned(self) -> None:
        """Exit codes are stable across the 0.x window — any bump
        is breaking (rule AP-04)."""
        assert EXIT_SUCCESS == 0
        assert EXIT_CONFIG_ERROR == 1
        assert EXIT_OPERATION_ERROR == 2
        assert EXIT_PROVIDER_ERROR == 5
        assert EXIT_UNKNOWN == 10


class TestRunAndTranslate:
    def test_success_returns_zero(self) -> None:
        def main(argv: list[str]) -> int:
            return EXIT_SUCCESS

        rc = run_and_translate(main, [])
        assert rc == EXIT_SUCCESS

    def test_typed_exception_translates_to_exit_code(self) -> None:
        def main(argv: list[str]) -> int:
            raise AppConfigError("config broken")

        rc = run_and_translate(main, [])
        assert rc == EXIT_CONFIG_ERROR

    def test_resource_not_found_maps_to_provider(self) -> None:
        def main(argv: list[str]) -> int:
            raise ResourceNotFoundError("vm-99")

        rc = run_and_translate(main, [])
        assert rc == EXIT_PROVIDER_ERROR

    def test_operation_error_maps_correctly(self) -> None:
        def main(argv: list[str]) -> int:
            raise OperationError("the operation failed")

        rc = run_and_translate(main, [])
        assert rc == EXIT_OPERATION_ERROR


class TestSubcommandRegistry:
    """Consumers register subcommands against the same registry
    the four built-ins (explain/validate/doctor/migrate) use."""

    def test_long_and_short_aliases_are_same_function(self) -> None:
        """v0.4.1 V04-02 — ``subcommand is register_cli_subcommand``."""
        assert subcommand is register_cli_subcommand

    def test_register_then_dispatch(self) -> None:
        called = {"args": None}

        @subcommand("public_surface_test_cmd", help="public-surface smoke")
        def _h(args: object) -> int:
            called["args"] = args
            return 0

        try:
            parser = build_cli(prog="testapp")
            ns = parser.parse_args(["public_surface_test_cmd"])
            rc = dispatch(ns, parser=parser)
            assert rc == 0
            assert called["args"] is not None
        finally:
            unregister_cli_subcommand("public_surface_test_cmd")


class TestBuildCli:
    def test_default_prog(self) -> None:
        parser = build_cli()
        assert parser.prog == "mgf-common"

    def test_custom_prog_for_consumer_cli(self) -> None:
        parser = build_cli(prog="myapp")
        assert parser.prog == "myapp"


class TestProvenanceHelpers:
    """v0.4 Phase A promoted these from private."""

    def test_env_vars_known_to_mgf_returns_sorted_pairs(self) -> None:
        result = env_vars_known_to_mgf()
        # Returns list of (var_name, current_value | None)
        assert isinstance(result, list)
        assert all(isinstance(item, tuple) and len(item) == 2 for item in result)
        # Sorted alphabetically
        names = [name for name, _ in result]
        assert names == sorted(names)

    def test_field_provenance_returns_per_subm_dict(self) -> None:
        from mgf.common.settings import MgfSettings

        prov = field_provenance(MgfSettings())
        # Returns {sub_name: {field: FieldSource}, "_root": {...}}
        assert "logging" in prov
        assert "_root" in prov
