from __future__ import annotations

import enum
import os
import platform
import sysconfig
from pathlib import PurePosixPath
from textwrap import dedent
from typing import NewType


# enum.StrEnum is Python 3.11+; pipx supports 3.10.
class FetchPythonOptions(str, enum.Enum):
    ALWAYS = "always"
    MISSING = "missing"
    NEVER = "never"

    def __str__(self) -> str:
        return self.value


PIPX_SHARED_PTH = "pipx_shared.pth"
TEMP_VENV_EXPIRATION_THRESHOLD_DAYS = 14
MINIMUM_PYTHON_VERSION = "3.10"
MAN_SECTIONS = [f"man{i}" for i in range(1, 10)]
# the directories a wheel ships completion scripts in, relative to the data scheme's share/, and the layout bash and
# fish already read from under a user data directory
COMPLETION_SECTIONS = [
    PurePosixPath("bash-completion/completions"),
    PurePosixPath("zsh/site-functions"),
    PurePosixPath("fish/vendor_completions.d"),
]


_FETCH_MISSING_PYTHON_RAW = os.environ.get("PIPX_FETCH_MISSING_PYTHON")
_FETCH_PYTHON_RAW = os.environ.get("PIPX_FETCH_PYTHON")
_FALSY = ("", "0", "false", "f", "no", "n", "off")


def _compute_fetch_python(missing_raw: str | None, python_raw: str | None) -> tuple[FetchPythonOptions, bool]:
    fetch_missing = missing_raw is not None and missing_raw.strip().lower() not in _FALSY
    raw = python_raw or (FetchPythonOptions.MISSING.value if fetch_missing else FetchPythonOptions.NEVER.value)
    try:
        return FetchPythonOptions(raw), False
    except ValueError:
        return FetchPythonOptions.NEVER, True


_FETCH_PYTHON, _FETCH_PYTHON_INVALID = _compute_fetch_python(_FETCH_MISSING_PYTHON_RAW, _FETCH_PYTHON_RAW)

_COOLDOWN_RAW = os.environ.get("PIPX_COOLDOWN")


def _compute_cooldown(raw: str | None) -> tuple[int | None, bool]:
    if raw is None or not (stripped := raw.strip()):
        return None, False
    try:
        days = int(stripped)
    except ValueError:
        return None, True
    return (days, False) if days >= 0 else (None, True)


_COOLDOWN, _COOLDOWN_INVALID = _compute_cooldown(_COOLDOWN_RAW)


ExitCode = NewType("ExitCode", int)
# pipx shell exit codes
EXIT_CODE_OK = ExitCode(0)
EXIT_CODE_INJECT_ERROR = ExitCode(1)
EXIT_CODE_UNINJECT_ERROR = ExitCode(1)
EXIT_CODE_INSTALL_VENV_EXISTS = ExitCode(0)
EXIT_CODE_LIST_PROBLEM = ExitCode(1)
EXIT_CODE_UNINSTALL_VENV_NONEXISTENT = ExitCode(1)
EXIT_CODE_UNINSTALL_ERROR = ExitCode(1)
EXIT_CODE_REINSTALL_VENV_NONEXISTENT = ExitCode(1)
EXIT_CODE_REINSTALL_INVALID_PYTHON = ExitCode(1)
EXIT_CODE_SPECIFIED_PYTHON_EXECUTABLE_NOT_FOUND = ExitCode(1)


def is_windows() -> bool:
    return platform.system() == "Windows"


def is_macos() -> bool:
    return platform.system() == "Darwin"


def is_linux() -> bool:
    return platform.system() == "Linux"


def is_mingw() -> bool:
    return sysconfig.get_platform().startswith("mingw")


WINDOWS: bool = is_windows()
MACOS: bool = is_macos()
LINUX: bool = is_linux()
MINGW: bool = is_mingw()

completion_instructions = dedent(
    """
If you encountered register-python-argcomplete command not found error,
or if you are using zipapp, run

    pipx install argcomplete

before running any of the following commands.

Add the appropriate command to your shell's config file
so that it is run on startup. You will likely have to restart
or re-login for the autocompletion to start working.

bash:
    eval "$(register-python-argcomplete pipx)"

zsh:
    To activate completions in zsh, first make sure compinit is marked for
    autoload and run autoload:

    autoload -U compinit && compinit

    Afterwards you can enable completions for pipx:

    eval "$(register-python-argcomplete pipx)"

    NOTE: If your version of argcomplete is earlier than v3, you may need to
    have bashcompinit enabled in zsh by running:

    autoload -U bashcompinit
    bashcompinit


tcsh:
    eval `register-python-argcomplete --shell tcsh pipx`

fish:
    # Not required to be in the config file, only run once
    register-python-argcomplete --shell fish pipx >~/.config/fish/completions/pipx.fish

"""
)
