# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
# PYTHON_ARGCOMPLETE_OK
import sys
import os
import traceback
from typing import TYPE_CHECKING
from pathlib import Path

from pmb.helpers.exceptions import BuildFailedError, NonBugError

if TYPE_CHECKING:
    from pmb.types import PmbArgs

from . import config
from . import parse
from .config import init as config_init, require_programs
from .helpers import frontend as frontend
from .helpers import logging
from .helpers import mount
from .helpers import other
from .helpers import status
from .core import Chroot, Config
from .core.context import get_context
from .commands import run_command

# pmbootstrap version
__version__ = "3.3.1"

# Python version check
# === CHECKLIST FOR UPGRADING THE REQUIRED PYTHON VERSION ===
# * .ci/vermin.sh
# * README.md
# * docs/usage.rst
# * pmb/__init__.py (you are here)
# * pyproject.toml
# * when upgrading to python 3.11: pmb/helpers/toml.py and remove this line
version = sys.version_info
if version < (3, 10):
    print("You need at least Python 3.10 to run pmbootstrap")
    print("(You are running it with Python " + str(version.major) + "." + str(version.minor) + ")")
    sys.exit()


def print_log_hint() -> None:
    context = get_context(allow_failure=True)
    if context and context.details_to_stdout:
        return
    log = context.log if context else Config().work / "log.txt"
    # Hints about the log file (print to stdout only)
    log_hint = "Run 'pmbootstrap log' for details."
    if not os.path.exists(log):
        log_hint += (
            " Alternatively you can use '--details-to-stdout' to get more"
            " output, e.g. 'pmbootstrap --details-to-stdout init'."
        )
    print()
    print(log_hint)


def main() -> int:
    # Wrap everything to display nice error messages

    args: PmbArgs
    try:
        # Parse arguments, set up logging
        args = parse.arguments()
        context = get_context()
        os.umask(0o22)

        # Store script invocation command
        os.environ["PMBOOTSTRAP_CMD"] = sys.argv[0]

        # Sanity checks
        other.check_grsec()
        if not args.as_root and os.geteuid() == 0:
            raise RuntimeError("Do not run pmbootstrap as root!")

        # Check for required programs (and find their absolute paths)
        require_programs()

        # Initialize or require config
        if args.action == "init":
            config_init.frontend(args)
            return 0
        elif not os.path.exists(args.config):
            if args.config != config.defaults["config"]:
                raise NonBugError(f"Couldn't find file passed with --config: {args.config}")
            old_config = (
                Path(os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser("~/.config"))
                / "pmbootstrap.cfg"
            )
            if os.path.exists(old_config):
                raise NonBugError(
                    f"Thanks for upgrading to pmbootstrap {__version__}!"
                    " The config file format has changed, please generate a new config with"
                    " 'pmbootstrap init'."
                )
            raise NonBugError(
                "Run 'pmbootstrap init' first to generate a config file (or use --config)."
            )
        elif not os.path.exists(context.config.work):
            raise NonBugError("Work path not found, please run 'pmbootstrap init' to create it.")

        # Migrate work folder if necessary
        if args.action not in ["shutdown", "zap", "log"]:
            other.migrate_work_folder()

        # Run the function with the action's name (in pmb/helpers/frontend.py)
        if args.action:
            run_command(args)
        else:
            logging.info("Run pmbootstrap -h for usage information.")

        # Still active notice
        if mount.ismount(Chroot.native() / "dev"):
            logging.info("NOTE: chroot is still active (use 'pmbootstrap shutdown' as necessary)")
        logging.info("DONE!")

    except KeyboardInterrupt:
        print("\nCaught KeyboardInterrupt, exiting …")
        sys.exit(130)  # SIGINT(2) + 128

    except NonBugError as exception:
        logging.error(f"ERROR: {exception}")
        return 2

    except BuildFailedError as exception:
        logging.error(f"ERROR: {exception}")
        print_log_hint()
        return 3

    except Exception as e:
        # Dump log to stdout when args (and therefore logging) init failed
        can_print_status = get_context(allow_failure=True) is not None
        if "args" not in locals():
            import logging as pylogging

            pylogging.getLogger().setLevel(logging.DEBUG)
            can_print_status = False

        logging.info("ERROR: " + str(e))
        logging.info("See also: <https://postmarketos.org/troubleshooting>")
        logging.debug(traceback.format_exc())

        print_log_hint()
        print()
        print("Before you report this error, ensure that pmbootstrap is up to date.")
        print(
            "Find the latest version here: https://gitlab.postmarketos.org/postmarketOS/pmbootstrap/-/tags"
        )
        print(f"Your version: {__version__}")
        if can_print_status:
            status.print_status()
        return 1

    return 0


if __name__ == "__main__":
    sys.exit(main())
