# src/pyhabitat/console.py
from __future__ import annotations
import os
import sys
import subprocess
import getpass
import shutil

from ._compat import cache
from .environment import on_windows

__all__ = [
    "is_likely_ci_or_non_interactive",
    "interactive_terminal_is_available",
    "user_darrin_deyoung",
    "is_running_in_uvicorn",
    "can_spawn_shell"

]

def clear_shell_cache()->None:
    """Clear every @cache used in pyhabitat, and call from CLI using --clear-cache"""
    can_spawn_shell.cache_clear()
    can_spawn_shell_lite.cache_clear()

def is_likely_ci_or_non_interactive() -> bool:
    """
    Heuristic: are we probably in a CI/CD pipeline or non-interactive container?
    Returns True if we should avoid prompts (require all args instead).

    # Usage Example in your CLI logic:
    if is_likely_ci_or_non_interactive():
        # Require all required args (e.g. value must be positional)
        if value is None:
            raise typer.BadParameter("Value required in non-interactive/CI mode")
    else:
        # Safe to prompt
        value = typer.prompt("Secret value", hide_input=True)

    """
    # 1. Basic TTY check (fast fail if no tty)
    if not (sys.stdin.isatty() and sys.stdout.isatty()):
        return True  # definitely non-interactive

    # 2. Common CI env var fingerprints (high confidence)
    ci_markers = [
        "GITHUB_ACTIONS",     # == "true"
        "GITLAB_CI",          # == "true"
        "CIRCLECI",           # == "true"
        "TRAVIS",             # == "true"
        "JENKINS_URL",        # non-empty
        "TF_BUILD",           # Azure Pipelines
        "CI",                 # generic (many set to "true")
        "BUILD_ID",           # Jenkins, some others
        "CODEBUILD_BUILD_ID", # AWS
        "BITBUCKET_BUILD_NUMBER",
    ]

    for var in ci_markers:
        val = os.getenv(var)
        if val and val.lower() not in ("", "false", "0", "no"):
            return True

    # 3. Optional: Docker/container detection (if relevant for your tool)
    #    - File-based (classic, works in most Docker images)
    if os.path.exists("/.dockerenv"):
        return True

    #    - cgroup-based (more reliable in modern Docker / containerd / podman)
    try:
        with open("/proc/1/cgroup", "r") as f:
            if "docker" in f.read() or "kubepods" in f.read() or "containerd" in f.read():
                return True
    except Exception:
        pass  # ignore if /proc not available (e.g. non-Linux)

    # If none of the above → probably real interactive terminal
    return False




# --- TTY Check ---
def interactive_terminal_is_available():
    """
    Check if the script is running in an interactive terminal. 
    Assumpton: 
        If interactive_terminal_is_available() returns True, 
        then typer.prompt() or input() will work reliably,
        without getting lost in a log or lost entirely.
    
    Solution correctly identifies that true interactivity requires:
        (1) a TTY (potential) connection
        (2) the ability to execute
        (3) the ability to read I/O
        (4) ignores known limitatons in restrictive environments

    Jargon:
        A TTY, short for Teletypewriter or TeleTYpe, 
        is a conceptual or physical device that serves 
        as the interface for a user to interact with 
        a computer system.
    """
    
    # --- 1. Edge Case/Known Environment Check ---
    # Address walmart demo unit edge case, fast check, though this might hamstring othwrwise successful processes
    if user_darrin_deyoung():
        return False
    
    # --- 2. Core TTY Check (Is a terminal attached?) ---
    # Check if a tty is attached to stdin AND stdout. This is the minimum requirement.
    if not (sys.stdin.isatty() and sys.stdout.isatty()):
        return False
    
    # --- 3. Uvicorn/Server Occupancy Check (Crucial for your issue) ---
    # If the TTY is attached, but the process is currently serving an ASGI application 
    # (like Uvicorn running your FastAPI app), it is NOT interactively available for new CLI input.
    if is_running_in_uvicorn():
        # This prevents the CLI from "steamrolling" the prompts when the user presses Fetch.
        return False
    
    # Check of a new shell can be launched to print stuff
    if not can_spawn_shell():
        return False
    
    return sys.stdin.isatty() and sys.stdout.isatty()


@cache
def can_spawn_shell_lite()->bool: 
    """Check if a shell command can be executed successfully.""" 
    from .environment import on_windows
    return shutil.which('cmd.exe' if on_windows() else "sh") is not None

@cache
def can_spawn_shell(override_known:bool=False)->bool: 
    """Check if a shell command can be executed successfully.""" 
    from .environment import on_windows
    cmd = "cmd.exe /c exit 0" if on_windows() else "true"
    try:
        # Use a simple, universally applicable command with shell=True
        # 'true' on Linux/macOS, or a basic command on Windows via cmd.exe
        # A simple 'echo' or 'exit 0' would also work
        result = subprocess.run( 
            cmd,
            shell=True, # <--- ESSENTIAL for cross-platform reliability
            stdout=subprocess.PIPE, 
            stderr=subprocess.PIPE, 
            timeout=3,
        )
        success  = (result.returncode == 0)
        
    except subprocess.TimeoutExpired: 
        print("Shell spawn failed: TimeoutExpired")
        success = False
    except subprocess.SubprocessError: 
        print("Shell spawn failed: SubprocessError") 
        success = False
    except OSError: 
        print("Shell spawn failed: OSError (likely permission or missing binary)") 
        success = False
    return success

def is_running_in_uvicorn():
    # Uvicorn, Hypercorn, Daphne, etc.
    """
    Heuristic check to see if the current code is running inside a Uvicorn worker process.
    This is highly useful for context-aware interactivity checks.
    """
    return getattr(sys, '_uvicorn_workers', None) is not None

def user_darrin_deyoung():
    """Common demo unit undicator, edge case that is unable to launch terminal"""
    # Enable teating on non-Windows, non-demo systems
    #  where this function would otherwise return False.
    # Linux: `export USER_DARRIN_DEYOUNG=True`
    if os.getenv('USER_DARRIN_DEYOUNG','').lower() ==  "true":
        print("env var USER_DARRIN_DEYOUNG is set to True.")
        return True
    # Darrin Deyoung is the typical username on demo-mode Windows systems
    if not on_windows():
        return False
    username = getpass.getuser()
    return username.lower() == "darrin deyoung"
