# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html

"""Helpers for the packaged version of Anki."""

from __future__ import annotations

import contextlib
import os
import subprocess
import sys
from pathlib import Path

from anki.utils import is_mac, is_win


# ruff: noqa: F401
def first_run_setup() -> None:
    """Code run the first time after install/upgrade.

    Currently, we just import our main libraries and invoke
    mpv/lame on macOS, which is slow on the first run, and doing
    it this way shows progress being made.
    """

    if not is_mac:
        return

    # Import anki_audio first and spawn commands
    import anki_audio

    audio_pkg_path = Path(anki_audio.__file__).parent

    # Start mpv and lame commands concurrently
    processes = []
    for cmd_name in ["mpv", "lame"]:
        cmd_path = audio_pkg_path / cmd_name
        proc = subprocess.Popen(
            [str(cmd_path), "--version"],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
        )
        processes.append(proc)

    # Continue with other imports while commands run
    import concurrent.futures

    import bs4
    import flask
    import markdown
    import PyQt6.QtCore
    import PyQt6.QtGui
    import PyQt6.QtNetwork
    import PyQt6.QtQuick
    import PyQt6.QtWebChannel
    import PyQt6.QtWebEngineCore
    import PyQt6.QtWebEngineWidgets
    import PyQt6.QtWidgets
    import PyQt6.sip
    import requests
    import waitress

    import anki.collection

    from . import _macos_helper

    # Wait for both commands to complete
    for proc in processes:
        proc.wait()


def uv_binary() -> str | None:
    """Return the path to the uv binary."""
    return os.environ.get("ANKI_LAUNCHER_UV")


def launcher_root() -> str | None:
    """Return the path to the launcher root directory (AnkiProgramFiles)."""
    return os.environ.get("UV_PROJECT")


def venv_binary(cmd: str) -> str | None:
    """Return the path to a binary in the launcher's venv."""
    root = launcher_root()
    if not root:
        return None

    root_path = Path(root)
    if is_win:
        binary_path = root_path / ".venv" / "Scripts" / cmd
    else:
        binary_path = root_path / ".venv" / "bin" / cmd

    return str(binary_path)


def add_python_requirements(reqs: list[str]) -> tuple[bool, str]:
    """Add Python requirements to the launcher venv using uv add.

    Returns (success, output)"""

    binary = uv_binary()
    if not binary:
        return (False, "Not in packaged build.")

    uv_cmd = [binary, "add"] + reqs
    result = subprocess.run(uv_cmd, capture_output=True, text=True, check=False)

    if result.returncode == 0:
        root = launcher_root()
        if root:
            sync_marker = Path(root) / ".sync_complete"
            sync_marker.touch()

        return (True, result.stdout)
    else:
        return (False, result.stderr)


def launcher_executable() -> str | None:
    """Return the path to the Anki launcher executable."""
    return os.getenv("ANKI_LAUNCHER")


def trigger_launcher_run() -> None:
    """Create a trigger file to request launcher UI on next run."""
    try:
        root = launcher_root()
        if not root:
            return

        trigger_path = Path(root) / ".want-launcher"
        trigger_path.touch()
    except Exception as e:
        print(e)


def update_and_restart() -> None:
    """Update and restart Anki using the launcher."""
    from aqt import mw

    launcher = launcher_executable()
    assert launcher

    trigger_launcher_run()

    with contextlib.suppress(ResourceWarning):
        env = os.environ.copy()
        env["ANKI_LAUNCHER_WANT_TERMINAL"] = "1"
        # fixes a bug where launcher fails to appear if opening it
        # straight after updating
        if "GNOME_TERMINAL_SCREEN" in env:
            del env["GNOME_TERMINAL_SCREEN"]
        creationflags = 0
        if sys.platform == "win32":
            creationflags = (
                subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS
            )
        # On Windows 10, changing the handles breaks ANSI display
        io = None if sys.platform == "win32" else subprocess.DEVNULL

        subprocess.Popen(
            [launcher],
            start_new_session=True,
            stdin=io,
            stdout=io,
            stderr=io,
            env=env,
            creationflags=creationflags,
        )

    mw.app.quit()
