"""Deploy bootstrap helpers for BYOM entrypoints."""

from __future__ import annotations

import sys
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any


if TYPE_CHECKING:
    from collections.abc import Callable

from .server import start_deploy_server
from .status_handler import DeployStatusReporter, safe_log_error


def default_action_tracker_factory(action_id: str) -> Any:
    """Create an ActionTracker lazily to avoid import-time hard dependency.

    Args:
        action_id: Platform action identifier for deployment tracking.

    Returns:
        Instantiated ActionTracker object.
    """
    from matrice.actionTracker import ActionTracker  # type: ignore[reportMissingImports]

    return ActionTracker(action_id)


@dataclass(frozen=True)
class DeployBootstrapConfig:
    """Configuration for deploy bootstrap behavior."""

    backend: str = "matrice_deploy"
    location: str = "deploy/main"
    emit_ack_status: bool = True
    emit_model_ready_status: bool = True
    emit_started_status: bool = True
    server_factory: Callable[[Callable[[Any], Any], Callable[[Any, bytes], Any], str, int], Any] | None = None


def run_deploy(
    *,
    action_id: str,
    port: int,
    load_model: Callable[[Any], Any],
    predict: Callable[[Any, bytes], Any],
    action_tracker_factory: Callable[[str], Any] | None = None,
    config: DeployBootstrapConfig | None = None,
) -> Any:
    """Run deployment bootstrap flow and start the configured server.

    Args:
        action_id: Platform action identifier used by ActionTracker.
        port: External port where deployment server should listen.
        load_model: Callable that loads and returns model state.
        predict: Callable that performs inference from model and bytes input.
        action_tracker_factory: Optional factory for tracker instantiation.
        config: Optional bootstrap configuration overrides.

    Returns:
        Started deployment server instance.
    """
    bootstrap_config = config or DeployBootstrapConfig()
    tracker_factory = action_tracker_factory or default_action_tracker_factory

    action_tracker = tracker_factory(action_id)
    reporter = DeployStatusReporter(action_tracker)
    if bootstrap_config.emit_ack_status:
        reporter.acknowledged()

    try:
        if bootstrap_config.emit_model_ready_status:
            reporter.model_ready()
        server = start_deploy_server(
            load_model=load_model,
            predict=predict,
            action_id=action_id,
            port=int(port),
            backend=bootstrap_config.backend,
            server_factory=bootstrap_config.server_factory,
        )
        if bootstrap_config.emit_started_status:
            reporter.started()
    except Exception as error:
        reporter.failed(f"Error in model deployment : {error!s}")
        safe_log_error(
            action_tracker,
            __file__,
            bootstrap_config.location,
            f"Error in deployment bootstrap: {error!s}",
        )
        raise
    return server


def build_cli_main(
    *,
    load_model: Callable[[Any], Any],
    predict: Callable[[Any, bytes], Any],
    action_tracker_factory: Callable[[str], Any] | None = None,
    config: DeployBootstrapConfig | None = None,
) -> Callable[[str, int], None]:
    """Create a deploy entrypoint function with bound model callbacks.

    Args:
        load_model: Callable that loads and returns model state.
        predict: Callable that performs inference from model and bytes input.
        action_tracker_factory: Optional tracker factory override.
        config: Optional bootstrap configuration overrides.

    Returns:
        Function with signature ``main(action_id, port)``.
    """

    def main(action_id: str, port: int) -> None:
        """Invoke deployment bootstrap using previously bound callbacks.

        Args:
            action_id: Platform action identifier for deployment tracking.
            port: External port where deployment server should listen.

        Returns:
            None.
        """
        run_deploy(
            action_id=action_id,
            port=int(port),
            load_model=load_model,
            predict=predict,
            action_tracker_factory=action_tracker_factory,
            config=config,
        )

    return main


def run_main_from_argv(
    argv: list[str],
    *,
    main: Callable[[str, int], None],
    usage: str = "Usage: python3 deploy.py <action_status_id> <port>",
) -> None:
    """Parse CLI arguments and execute the provided deploy entrypoint.

    Args:
        argv: Raw CLI argument vector (typically ``sys.argv``).
        main: Entrypoint callable expecting ``(action_id, port)``.
        usage: Usage text shown when argument count is invalid.

    Returns:
        None.
    """
    if len(argv) != 3:
        raise SystemExit(usage)

    try:
        main(argv[1], int(argv[2]))
    except Exception as error:
        raise SystemExit(f"Deployment failed: {error!s}") from error


def run_main_and_exit(
    *,
    main: Callable[[str, int], None],
    usage: str = "Usage: python3 deploy.py <action_status_id> <port>",
) -> None:
    """Run deploy entrypoint directly from process arguments.

    Args:
        main: Entrypoint callable expecting ``(action_id, port)``.
        usage: Usage text shown when argument count is invalid.

    Returns:
        None.
    """
    run_main_from_argv(sys.argv, main=main, usage=usage)
