"""Git utilities for codeflash."""

from __future__ import annotations

import logging
import sys
import time
from functools import cache
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from collections.abc import Callable
    from types import ModuleType

    import git as git_types

log = logging.getLogger(__name__)

git: ModuleType


def __getattr__(name: str) -> Any:
    """Lazy import for the ``git`` package."""
    if name == "git":
        import git as _git  # noqa: PLC0415

        globals()["git"] = _git
        return _git
    msg = f"module {__name__!r} has no attribute {name!r}"
    raise AttributeError(msg)


def get_remote_url(
    repo: git_types.Repo | None = None,
    git_remote: str = "origin",
) -> str:
    """Return the URL of the given git remote."""
    repository: git_types.Repo = repo or git.Repo(  # noqa: F821
        search_parent_directories=True,
    )
    return repository.remote(name=git_remote).url


@cache
def get_repo_owner_and_name(
    repo: git_types.Repo | None = None,
    git_remote: str = "origin",
) -> tuple[str, str]:
    """Return (owner, repo_name) parsed from the git remote URL."""
    from ._platform import parse_repo_owner_and_name  # noqa: PLC0415

    return parse_repo_owner_and_name(get_remote_url(repo, git_remote))


def check_running_in_git_repo(module_root: str) -> bool:
    """Return *True* if *module_root* is inside a git repository."""
    try:
        _ = git.Repo(  # noqa: F821
            module_root,
            search_parent_directories=True,
        ).git_dir
    except git.InvalidGitRepositoryError:  # noqa: F821
        return False
    else:
        return True


def check_and_push_branch(
    repo: git_types.Repo,
    git_remote: str = "origin",
    *,
    wait_for_push: bool = False,
    confirm_fn: Callable[[str], bool] | None = None,
) -> bool:
    """Ensure the current branch is pushed to the remote.

    *confirm_fn*, when provided, is called with a prompt string and
    must return ``True`` to proceed with the push.  When ``None`` and
    the terminal is interactive the function logs a warning and returns
    ``False``.

    Previously used ``rich.prompt.Confirm.ask`` directly; now accepts
    a callback so core stays free of UI dependencies.
    """
    if repo.head.is_detached:
        log.warning(
            "HEAD is detached. Cannot push branch.",
        )
        return False

    try:
        current_branch = repo.active_branch
        current_branch_name = current_branch.name
    except (AttributeError, TypeError) as e:
        log.warning("Could not determine active branch: %s", e)
        return False

    remote = repo.remote(name=git_remote)

    if f"{git_remote}/{current_branch_name}" not in repo.refs:
        log.warning(
            "The branch '%s' is not pushed to the remote repository.",
            current_branch_name,
        )
        if sys.__stdin__ is None or not sys.__stdin__.isatty():
            log.warning(
                "Non-interactive shell detected. Branch will not be pushed.",
            )
            return False
        if confirm_fn is None:
            log.warning(
                "No confirmation callback provided."
                " Branch will not be pushed.",
            )
            return False
        if confirm_fn(
            f"Push the branch '{current_branch_name}'"
            f" to the remote repository?",
        ):
            remote.push(current_branch)
            log.info(
                "Branch '%s' has been pushed to %s.",
                current_branch_name,
                git_remote,
            )
            if wait_for_push:
                time.sleep(3)
            return True
        log.info(
            "Branch '%s' has not been pushed to %s.",
            current_branch_name,
            git_remote,
        )
        return False
    log.debug(
        "The branch '%s' is present in the remote repository.",
        current_branch_name,
    )
    return True
