"""POSIX execution strategy."""

from __future__ import annotations

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

from dekk.execution.os.shared import generated_timestamp, sh_escape_double, sh_quote

POSIX_OS_NAME = "posix"
POSIX_PATH_SEPARATOR = ":"
POSIX_PYTHON_COMMANDS = ("python3", "python")
POSIX_BIN_DIRNAME = "bin"
POSIX_PRIMARY_PYTHON = "python3"
POSIX_FALLBACK_PYTHON = "python"
POSIX_NPM_COMMANDS = ("npm",)
POSIX_PIP = "pip"
POSIX_LIB_DIRNAME = "lib"
POSIX_CMAKE_DIRNAME = "cmake"
POSIX_PATH_ENV = "PATH"
DARWIN_SYSTEM_NAME = "Darwin"
DARWIN_LIBRARY_PATH_ENV = "DYLD_LIBRARY_PATH"
POSIX_LIBRARY_PATH_ENV = "LD_LIBRARY_PATH"


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

    name: str = POSIX_OS_NAME
    path_separator: str = POSIX_PATH_SEPARATOR

    def wrapper_filename(self, name: str) -> str:
        return name

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

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

    def conda_runtime_paths(self, prefix: Path) -> tuple[Path, ...]:
        return (prefix / POSIX_BIN_DIRNAME,)

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

    def cmake_package_dir(self, prefix: Path, package: str) -> Path:
        return prefix / POSIX_LIB_DIRNAME / POSIX_CMAKE_DIRNAME / package

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

    def shared_library_path_var(self) -> str | None:
        if platform.system() == DARWIN_SYSTEM_NAME:
            return DARWIN_LIBRARY_PATH_ENV
        return POSIX_LIBRARY_PATH_ENV

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

    def venv_python(self, venv_path: Path) -> Path:
        bin_dir = self.venv_bin_dir(venv_path)
        primary = bin_dir / POSIX_PRIMARY_PYTHON
        return primary if primary.exists() else bin_dir / POSIX_FALLBACK_PYTHON

    def venv_pip(self, venv_path: Path) -> Path:
        return self.venv_bin_dir(venv_path) / POSIX_PIP

    def make_wrapper_executable(self, path: Path) -> None:
        path.chmod(path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    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] = [
            "#!/bin/sh",
            f"# Wrapper for {project_name}",
            f"# Generated by dekk on {generated_timestamp()}",
            f"# Regenerate with: dekk wrap {project_name} {target}"
            + (f" --python {python}" if python else ""),
            "#",
            "# This script bakes in the full project environment so the",
            "# tool can be invoked without manually activating anything.",
            "",
        ]

        if env_vars:
            lines.append("# --- Environment variables ---")
            for name, value in env_vars.items():
                if name == POSIX_PATH_ENV:
                    continue
                lines.append(f"export {name}={sh_quote(value)}")
            lines.append("")

        if prepend_vars:
            for name, value in prepend_vars.items():
                lines.append(f'export {name}="{sh_escape_double(value)}:${name}"')
            lines.append("")

        if path_prepends:
            lines.append("# --- PATH ---")
            joined = POSIX_PATH_SEPARATOR.join(path_prepends)
            lines.append(f'export {POSIX_PATH_ENV}="{sh_escape_double(joined)}:${POSIX_PATH_ENV}"')
            lines.append("")

        lines.append("# --- Exec (replaces this process) ---")
        if python:
            lines.append(f'exec {sh_quote(str(python))} {sh_quote(str(target))} "$@"')
        else:
            lines.append(f'exec {sh_quote(str(target))} "$@"')
        lines.append("")
        return "\n".join(lines)


__all__ = ["PosixDekkOS"]
