import asyncio
import os
import sys
from pathlib import Path

from src.constants import (
    ENV_GRACEFUL_SHUTDOWN_TIMEOUT,
    ENV_GRANT_TOKEN,
    ENV_HEALTH_CHECK_SERVER_ENABLED,
    ENV_HEALTH_CHECK_SERVER_PORT,
    ENV_TASK_BROKER_URI,
    ENV_TASK_TIMEOUT,
)

from tests.fixtures.test_constants import (
    GRACEFUL_SHUTDOWN_TIMEOUT,
    HEALTH_CHECK_PORT,
    LOCAL_TASK_BROKER_URL,
    TASK_TIMEOUT,
)


class TaskRunnerManager:
    """Responsible for managing the lifecycle of a task runner subprocess."""

    def __init__(
        self,
        task_broker_url: str = LOCAL_TASK_BROKER_URL,
        graceful_shutdown_timeout: float | None = None,
    ):
        self.task_broker_url = task_broker_url
        self.graceful_shutdown_timeout = graceful_shutdown_timeout
        self.subprocess: asyncio.subprocess.Process | None = None
        self.stdout_buffer: list[str] = []
        self.stderr_buffer: list[str] = []

    async def start(self):
        project_root = Path(__file__).parent.parent.parent
        runner_path = project_root / "src" / "main.py"

        env_vars = os.environ.copy()
        env_vars[ENV_GRANT_TOKEN] = "test_token"
        env_vars[ENV_TASK_BROKER_URI] = self.task_broker_url
        env_vars[ENV_TASK_TIMEOUT] = str(TASK_TIMEOUT)
        env_vars[ENV_HEALTH_CHECK_SERVER_ENABLED] = "true"
        env_vars[ENV_HEALTH_CHECK_SERVER_PORT] = str(HEALTH_CHECK_PORT)
        if self.graceful_shutdown_timeout is not None:
            env_vars[ENV_GRACEFUL_SHUTDOWN_TIMEOUT] = str(
                self.graceful_shutdown_timeout
            )
        env_vars["PYTHONPATH"] = str(project_root)

        self.subprocess = await asyncio.create_subprocess_exec(
            sys.executable,
            str(runner_path),
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
            env=env_vars,
            cwd=str(project_root),
        )

        asyncio.create_task(self._read_stdout())
        asyncio.create_task(self._read_stderr())

    def is_running(self) -> bool:
        return self.subprocess is not None and self.subprocess.returncode is None

    async def stop(self) -> None:
        if not self.subprocess or self.subprocess.returncode is not None:
            return

        self.subprocess.terminate()
        try:
            await asyncio.wait_for(
                self.subprocess.wait(), timeout=GRACEFUL_SHUTDOWN_TIMEOUT
            )
        except asyncio.TimeoutError:
            self.subprocess.kill()
            await self.subprocess.wait()

    async def _read_stdout(self):
        if not self.subprocess or not self.subprocess.stdout:
            return

        while True:
            line = await self.subprocess.stdout.readline()
            if not line:
                break
            self.stdout_buffer.append(line.decode().strip())

    async def _read_stderr(self):
        if not self.subprocess or not self.subprocess.stderr:
            return

        while True:
            line = await self.subprocess.stderr.readline()
            if not line:
                break
            self.stderr_buffer.append(line.decode().strip())
