from __future__ import annotations

import subprocess
import sys
import uuid
from pathlib import Path
from typing import Any, Callable, Awaitable

from aicd.tools.process_tool import ProcessTool, windows_cmd_argv


class ScriptTool:
    def __init__(
        self,
        scripts_dir: Path,
        process: ProcessTool,
    ) -> None:
        self._dir = scripts_dir
        self._dir.mkdir(parents=True, exist_ok=True)
        self._process = process

    def _path_for(self, ext: str) -> Path:
        return self._dir / f"scratch_{uuid.uuid4().hex}{ext}"

    async def run_python(
        self,
        source: str,
        *,
        on_line: Callable[[str], Awaitable[None]] | None = None,
        timeout_sec: float | None = 300,
    ) -> dict[str, Any]:
        p = self._path_for(".py")
        p.write_text(source, encoding="utf-8")
        py = sys.executable
        return await self._process.run_command(
            [py, str(p)],
            timeout_sec=timeout_sec,
            on_line=on_line,
        )

    async def run_powershell(
        self,
        source: str,
        *,
        on_line: Callable[[str], Awaitable[None]] | None = None,
        timeout_sec: float | None = 300,
    ) -> dict[str, Any]:
        if sys.platform != "win32":
            return {"ok": False, "error": "PowerShell runner only on Windows"}
        p = self._path_for(".ps1")
        p.write_text(source, encoding="utf-8")
        return await self._process.run_command(
            [
                "powershell.exe",
                "-NoProfile",
                "-ExecutionPolicy",
                "Bypass",
                "-File",
                str(p),
            ],
            timeout_sec=timeout_sec,
            on_line=on_line,
        )

    async def run_cmd(
        self,
        batch: str,
        *,
        on_line: Callable[[str], Awaitable[None]] | None = None,
        timeout_sec: float | None = 300,
    ) -> dict[str, Any]:
        if sys.platform != "win32":
            return {"ok": False, "error": "CMD runner only on Windows"}
        p = self._path_for(".cmd")
        p.write_text(batch, encoding="utf-8")
        quoted = subprocess.list2cmdline([str(p)])
        return await self._process.run_command(
            windows_cmd_argv(quoted),
            timeout_sec=timeout_sec,
            on_line=on_line,
        )

    async def run_bash(
        self,
        source: str,
        *,
        bash_path: str = "bash",
        on_line: Callable[[str], Awaitable[None]] | None = None,
        timeout_sec: float | None = 300,
    ) -> dict[str, Any]:
        p = self._path_for(".sh")
        p.write_text(source, encoding="utf-8")
        return await self._process.run_command(
            [bash_path, str(p)],
            timeout_sec=timeout_sec,
            on_line=on_line,
        )
