"""Layer 2 — A small CLI app: argparse + Settings + logging + use case.

Realistic small-app shape: argparse for the entry point, a typed
Settings class loaded once at the top, a single use-case function
called with the settings, structured logging, exit-code mapping
on typed exceptions.
"""

from __future__ import annotations

import argparse
import logging
import os
import sys
import tempfile

import mgf.common
from mgf.common.config import BaseAppSettings, settings_config
from mgf.common.exceptions import AppError, OperationError
from mgf.common.settings import LoggingSettings, MgfSettings

# ── Typed config ─────────────────────────────────────────────────


class Settings(BaseAppSettings):
    model_config = settings_config(env_prefix="MYAPP_")
    backend_uri: str = "qemu:///system"
    log_level: str = "INFO"


# ── A use case that may raise typed exceptions ───────────────────


def greet(LOG: logging.Logger, name: str) -> str:
    """Greet someone. Raises OperationError if name is empty."""
    if not name:
        raise OperationError("name cannot be empty; pass --name <value>")
    # NOTE: `name` is reserved by Python's LogRecord; use a distinct
    # field name like `subject` for `extra={...}` to avoid KeyError.
    LOG.info("greeting", extra={"subject": name})
    return f"hello, {name}"


# ── CLI entry point with exit-code mapping ───────────────────────


EXIT_OK = 0
EXIT_OPERATION_ERROR = 2
EXIT_UNKNOWN = 10


def cli_main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(prog="myapp")
    parser.add_argument("--name", default="world")
    parser.add_argument("--log-level", default=None)
    args = parser.parse_args(argv)

    # Typed settings; CLI flags override the config defaults.
    settings = Settings()
    if args.log_level is not None:
        settings = settings.model_copy(update={"log_level": args.log_level})

    # bootstrap() wires identity + logging + OTel + excepthooks.
    with mgf.common.bootstrap(
        app_name="myapp",
        app_version="0.1.0",
        settings=MgfSettings(
            logging=LoggingSettings(level=settings.log_level, format="auto"),
        ),
    ):
        LOG = logging.getLogger("myapp")

        # Top-level handler maps each typed category to an exit code.
        try:
            result = greet(LOG, args.name)
            print(result)
            return EXIT_OK
        except OperationError as e:
            print(f"operation failed: {e}", file=sys.stderr)
            return EXIT_OPERATION_ERROR
        except AppError as e:
            print(f"error: {e}", file=sys.stderr)
            return EXIT_UNKNOWN


def main() -> None:
    """Entry point used by both `python script.py` and the smoke test."""
    with tempfile.TemporaryDirectory() as tmp:
        os.environ["XDG_STATE_HOME"] = tmp

        # Demo: happy path with default --name
        rc = cli_main([])
        print(f"  exit code: {rc}")

        # Demo: sad path — empty name triggers OperationError
        rc = cli_main(["--name", ""])
        print(f"  exit code (empty name): {rc}")


if __name__ == "__main__":
    main()
