from __future__ import annotations

import logging
import os
from typing import Optional, Union, cast

from kraken.core import Project, Property, Supplier, Task, TaskStatus
from nr.python.environment.virtualenv import get_current_venv

from ..buildsystem import PythonBuildSystem
from ..settings import python_settings

logger = logging.getLogger(__name__)


class InstallTask(Task):
    build_system: Property[Optional[PythonBuildSystem]]
    always_use_managed_env: Property[bool]

    def prepare(self) -> TaskStatus | None:
        venv = get_current_venv(os.environ)
        if not self.always_use_managed_env.get() and venv:
            return TaskStatus.skipped("using current virtual env (%s)" % venv.path)
        build_system = self.build_system.get()
        if not build_system:
            return TaskStatus.skipped("no Python build system configured")
        if not build_system.supports_managed_environments():
            return TaskStatus.skipped(
                "current build system does not supported managed environment (%s)" % type(build_system).__name__
            )
        managed_environment = build_system.get_managed_environment()
        if managed_environment.exists():
            return TaskStatus.skipped("managed environment exists (%s)" % managed_environment.get_path())
        return TaskStatus.pending()

    def execute(self) -> TaskStatus:
        build_system = self.build_system.get()
        if not build_system:
            logger.error("no build system configured")
            return TaskStatus.failed("no build system configured")
        managed_environment = build_system.get_managed_environment()
        managed_environment.install(python_settings(self.project))
        return TaskStatus.succeeded()


def install(project: Project | None = None) -> InstallTask:
    """Get or create the `pythonInstall` task for the given project.

    The install task relies on the build system configured in the Python project settings."""

    name = "pythonInstall"

    project = project or Project.current()
    task = cast(Union[InstallTask, None], project.tasks().get(name))
    if task is None:
        task = project.do(name, InstallTask, default=False)
        task.build_system.set(Supplier.of_callable(lambda: python_settings(project).build_system))
        task.always_use_managed_env.set(Supplier.of_callable(lambda: python_settings(project).always_use_managed_env))

    return task
