from __future__ import annotations

import os
from pathlib import Path
from subprocess import Popen

from bmde.core import logging
from bmde.core.exec import ExecOptions

from ...config.loader import load_settings
from ...core.logging import configure_logging_from_settings
from ...core.spec_opts import SpecExecOpts
from ...core.types import BackendOptions
from .service import BuildService
from .settings import BuildSettings
from .spec import BuildSpec, BuildSpecOpts

log = logging.get_logger(__name__)


def create_build_spec(
    d: Path | None,
    arguments: list[str] | None = None,
    backend: BackendOptions | None = None,
    background: bool = False,
    interactive: bool = True,
    dry_run: bool = False,
    entrypoint: Path | None = None,
    settings: BuildSettings | None = None,
) -> BuildSpec:
    """
    Creates the BuildSpec from the provided arguments.
    """
    if d is None:
        d = Path(os.getcwd())

    if settings is None:
        settings = BuildSettings()

    # Default args are prepended to CLI args
    passed_args: list[str] = []
    if arguments is not None:
        passed_args += arguments
    if settings.execution_settings.arguments is not None:
        passed_args += settings.execution_settings.arguments

    return BuildSpec(
        BuildSpecOpts=BuildSpecOpts(
            d=d,
        ),
        SpecExecOpts=SpecExecOpts(
            backend=backend if backend is not None else (settings.execution_settings.backend
                                                         if settings.execution_settings.backend is not None else
                                                         BackendOptions.DOCKER),
            entrypoint=(
                entrypoint
                if entrypoint is not None
                else settings.execution_settings.entrypoint
            ),
            arguments=passed_args,
            dry_run=(
                dry_run if dry_run is not None else settings.execution_settings.dry_run
            ),
            background=(
                background
                if background is not None
                else settings.execution_settings.background
            ),
            interactive=(
                interactive
                if interactive is not None
                else settings.execution_settings.interactive
            ),
        ),
    )


def execute_build(spec: BuildSpec) -> int | Popen[bytes]:
    """
    Executes the BuildSpec.
    """
    return BuildService().run(
        spec.BuildSpecOpts,
        ExecOptions(
            arguments=spec.SpecExecOpts.arguments,
            dry_run=spec.SpecExecOpts.dry_run,
            background=spec.SpecExecOpts.background,
            interactive=spec.SpecExecOpts.interactive,
            backend=spec.SpecExecOpts.backend,
            entrypoint=spec.SpecExecOpts.entrypoint
        ),
    )


def build_command(
    d: Path | None,
    arguments: list[str] | None = None,
    backend: BackendOptions | None = None,
    background: bool = False,
    interactive: bool = True,
    dry_run: bool = False,
    entrypoint: Path | None = None,
    settings: BuildSettings | None = None,
) -> int | Popen[bytes]:
    """
    CLI Entrypoint. Orchestrates spec creation and execution.
    """
    if settings is None:
        full_settings = load_settings()
        configure_logging_from_settings(
            settings=full_settings
        )
        settings = full_settings.build

    spec = create_build_spec(
        d=d,
        arguments=arguments,
        backend=backend,
        background=background,
        interactive=interactive,
        dry_run=dry_run,
        entrypoint=entrypoint,
        settings=settings,
    )

    return execute_build(spec)
