"""Windows execution strategy."""

from __future__ import annotations

import subprocess
from collections.abc import Mapping, Sequence
from dataclasses import dataclass
from pathlib import Path

from dekk.execution.os.shared import cmd_escape, generated_timestamp

WINDOWS_OS_NAME = "windows"
WINDOWS_PATH_SEPARATOR = ";"
WINDOWS_WRAPPER_EXTENSIONS = {".cmd", ".bat"}
WINDOWS_DEFAULT_WRAPPER_EXTENSION = ".cmd"
WINDOWS_PYTHON_COMMANDS = ("python.exe", "python")
WINDOWS_NPM_COMMANDS = ("npm.cmd", "npm.exe", "npm")
WINDOWS_LIBRARY_DIRNAME = "Library"
WINDOWS_LIB_DIRNAME = "lib"
WINDOWS_CMAKE_DIRNAME = "cmake"
WINDOWS_BIN_DIRNAME = "bin"
WINDOWS_SCRIPTS_DIRNAME = "Scripts"
WINDOWS_MINGW_DIRNAME = "mingw-w64"
WINDOWS_USR_DIRNAME = "usr"
WINDOWS_PRIMARY_PYTHON = "python.exe"
WINDOWS_FALLBACK_PYTHON = "python"
WINDOWS_PRIMARY_PIP = "pip.exe"
WINDOWS_FALLBACK_PIP = "pip"
WINDOWS_PATH_ENV = "PATH"


@dataclass(frozen=True)
class WindowsDekkOS:
    """Windows behavior for wrappers, venv layout, and runtime paths."""

    name: str = WINDOWS_OS_NAME
    path_separator: str = WINDOWS_PATH_SEPARATOR

    def wrapper_filename(self, name: str) -> str:
        suffix = Path(name).suffix.lower()
        if suffix in WINDOWS_WRAPPER_EXTENSIONS or suffix:
            return name
        return f"{name}{WINDOWS_DEFAULT_WRAPPER_EXTENSION}"

    def python_command_candidates(self) -> tuple[str, ...]:
        return WINDOWS_PYTHON_COMMANDS

    def npm_command_candidates(self) -> tuple[str, ...]:
        return WINDOWS_NPM_COMMANDS

    def conda_runtime_paths(self, prefix: Path) -> tuple[Path, ...]:
        return (
            prefix,
            prefix / WINDOWS_LIBRARY_DIRNAME / WINDOWS_MINGW_DIRNAME / WINDOWS_BIN_DIRNAME,
            prefix / WINDOWS_LIBRARY_DIRNAME / WINDOWS_USR_DIRNAME / WINDOWS_BIN_DIRNAME,
            prefix / WINDOWS_LIBRARY_DIRNAME / WINDOWS_BIN_DIRNAME,
            prefix / WINDOWS_SCRIPTS_DIRNAME,
            prefix / WINDOWS_BIN_DIRNAME,
        )

    def cmake_library_dir(self, prefix: Path) -> Path:
        return prefix / WINDOWS_LIBRARY_DIRNAME / WINDOWS_LIB_DIRNAME

    def cmake_package_dir(self, prefix: Path, package: str) -> Path:
        return (
            prefix / WINDOWS_LIBRARY_DIRNAME / WINDOWS_LIB_DIRNAME
            / WINDOWS_CMAKE_DIRNAME / package
        )

    def cmake_runtime_dir(self, prefix: Path) -> Path:
        return prefix / WINDOWS_LIBRARY_DIRNAME / WINDOWS_BIN_DIRNAME

    def shared_library_path_var(self) -> str | None:
        return None

    def venv_bin_dir(self, venv_path: Path) -> Path:
        return venv_path / WINDOWS_SCRIPTS_DIRNAME

    def venv_python(self, venv_path: Path) -> Path:
        scripts_dir = self.venv_bin_dir(venv_path)
        primary = scripts_dir / WINDOWS_PRIMARY_PYTHON
        return primary if primary.exists() else scripts_dir / WINDOWS_FALLBACK_PYTHON

    def venv_pip(self, venv_path: Path) -> Path:
        scripts_dir = self.venv_bin_dir(venv_path)
        primary = scripts_dir / WINDOWS_PRIMARY_PIP
        return primary if primary.exists() else scripts_dir / WINDOWS_FALLBACK_PIP

    def make_wrapper_executable(self, path: Path) -> None:
        return None

    def render_wrapper(
        self,
        *,
        target: Path,
        env_vars: Mapping[str, str],
        path_prepends: Sequence[str],
        project_name: str,
        prepend_vars: Mapping[str, str] | None = None,
        python: Path | None = None,
    ) -> str:
        lines: list[str] = [
            "@echo off",
            "setlocal",
            f"REM Wrapper for {project_name}",
            f"REM Generated by dekk on {generated_timestamp()}",
            f"REM Regenerate with: dekk wrap {project_name} {target}"
            + (f" --python {python}" if python else ""),
            "REM This script bakes in the full project environment.",
            "",
        ]

        for name, value in env_vars.items():
            if name == WINDOWS_PATH_ENV:
                continue
            lines.append(f'set "{name}={cmd_escape(value)}"')

        combined_prepends = dict(prepend_vars or {})
        if path_prepends:
            combined_prepends[WINDOWS_PATH_ENV] = WINDOWS_PATH_SEPARATOR.join(path_prepends)

        for name, value in combined_prepends.items():
            escaped = cmd_escape(value)
            lines.append(f"if defined {name} (")
            lines.append(f'  set "{name}={escaped}{WINDOWS_PATH_SEPARATOR}%{name}%"')
            lines.append(") else (")
            lines.append(f'  set "{name}={escaped}"')
            lines.append(")")

        command = [str(target)] if python is None else [str(python), str(target)]
        lines.append("call " + subprocess.list2cmdline(command) + " %*")
        lines.append("set EXIT_CODE=%ERRORLEVEL%")
        lines.append("endlocal & exit /b %EXIT_CODE%")
        lines.append("")
        return "\r\n".join(lines)


__all__ = ["WindowsDekkOS"]
