import logging
import os
from pathlib import Path
from typing import Any

from invoke.exceptions import Exit, Failure
from invoke.runners import Result, Runner
from invoke.tasks import task

logger = logging.getLogger(__name__)


def exec_command(c: Runner, command: str) -> int:
    res: Result | None = None
    try:
        res = c.run(command, pty=True, warn=True)
        return res.exited if res else 1
    except Failure:
        raise
    finally:
        msg = "\nExecuting done"
        msg += f" - Exit code: {res.exited}" if res else " - command never started"
        logger.debug(msg)


def exec_command_with_env(c: Runner, command: str, env: dict[str, str]) -> int:
    res: Result | None = None
    try:
        res = c.run(command, pty=True, warn=True, env=env)
        return res.exited if res else 1
    except Failure:
        raise
    finally:
        msg = "\nExecuting done"
        msg += f" - Exit code: {res.exited}" if res else " - command never started"
        logger.debug(msg)


def check_exit_codes(exit_codes: list[int | None]) -> None:
    for exit_code in exit_codes:
        if exit_code is not None and exit_code > 0:
            raise Exit(code=exit_code)


@task
def get_api_key(c: Runner) -> str | None:
    api_key_file = Path("../abraxas/.api_key")
    if api_key_file.exists():
        return api_key_file.read_text().strip()
    return None


@task
def lint(c: Runner, fix: bool = False) -> None:
    format_command = "uv run ruff format ."
    check_command = "uv run ruff check ."
    if fix:
        check_command += " --fix"
    if not fix:
        format_command += " --check"
    exit_codes: list[int | None] = []
    exit_codes.append(exec_command(c, format_command))
    exit_codes.append(exec_command(c, check_command))
    check_exit_codes(exit_codes)


@task
def typecheck(c: Runner) -> None:
    command = "uv run mypy mistralai/workflows/"
    exit_code = exec_command(c, command)
    check_exit_codes([exit_code])


@task
def check_api_surface(c: Runner, base_ref: str = "origin/main") -> None:
    public_api_check = (
        f"uv --project workflow_sdk run python workflow_sdk/scripts/check_public_api.py --base-ref {base_ref}"
    )
    griffe_check = (
        f"uv --project workflow_sdk run griffe check mistralai.workflows -b HEAD -a {base_ref} -s workflow_sdk"
    )
    exit_codes: list[int | None] = []
    exit_codes.append(exec_command(c, f"cd .. && {public_api_check}"))
    exit_codes.append(exec_command(c, f"cd .. && {griffe_check}"))
    if any(code is not None and code > 0 for code in exit_codes):
        YELLOW = "\033[93m"
        BOLD = "\033[1m"
        RESET = "\033[0m"
        print(f"\n{YELLOW}{'━' * 70}{RESET}")  # noqa: T201
        print(f"{BOLD}To proceed:{RESET}")  # noqa: T201
        print("  1. Document breaking changes in PR description")  # noqa: T201
        print(f"  2. Add the {BOLD}'breaking-sdk'{RESET} label to your PR")  # noqa: T201
        print(f"{YELLOW}{'━' * 70}{RESET}\n")  # noqa: T201
    check_exit_codes(exit_codes)


@task
def tests(
    c: Any,
    k: str | None = None,
    s: bool = False,
    v: bool = False,
    splits: str | None = None,
    group: str | None = None,
    store_durations: bool = False,
    n: str | None = None,
    timeout: float | None = None,
    enforce_determinism: bool = True,
) -> None:
    test_command = "uv run pytest tests/ -m 'not integration'"
    if s:
        test_command += " -s"
    if k:
        test_command += f" -k {k}"
    if v:
        test_command += " -v"
    if splits:
        test_command += f" --splits {splits}"
    if group:
        test_command += f" --group {group}"
    if store_durations:
        test_command += " --store-durations"
    if n:
        test_command += f" -n {n}"
    else:
        test_command += " -n auto"
    if timeout is not None:
        test_command += f" --timeout={timeout}"
    env = {"DEFAULT_ENFORCE_DETERMINISM": "1" if enforce_determinism else "0"}
    try:
        result = exec_command_with_env(c, test_command, env)
        check_exit_codes([result])
    except Failure:
        raise


@task
def integration_tests(
    c: Any,
    k: str | None = None,
    s: bool = False,
    v: bool = False,
    n: str | None = None,
) -> None:
    """
    Prerequisites: Dev environment must be running in abraxas (../abraxas).
    The MISTRAL_API_KEY environment variable must be set or ../abraxas/.api_key file must exist.
    """
    api_key = get_api_key(c)
    if not api_key:
        api_key = os.environ.get("MISTRAL_API_KEY")

    if not api_key:
        logger.error("MISTRAL_API_KEY environment variable is not set and ../abraxas/.api_key file not found.")
        logger.error("Please start the dev environment with 'cd ../abraxas && make dev'.")
        raise Exit(code=1)

    test_command = "uv run --extra webhook pytest tests/ plugins/webhook/tests/integration -m integration"
    if s:
        test_command += " -s"
    if k:
        test_command += f" -k {k}"
    if v:
        test_command += " -v"
    if n:
        test_command += f" -n {n}"
    else:
        test_command += " -n auto"

    env: dict[str, str] = {"MISTRAL_API_KEY": api_key, "DEPLOYMENT_NAME": "example-dev-worker"}

    try:
        result = exec_command_with_env(c, test_command, env)
        check_exit_codes([result])
    except Failure:
        raise
