import asyncio
import uuid
import time
from typing import Dict, Optional

from app.config import (
    CONTAINER_HOST,
    CONTAINER_RUNTIME,
    UERANSIM_CONTAINER_NAME,
    UERANSIM_ENV_FILE,
    UERANSIM_IMAGE,
    UERANSIM_NETWORK_MODE,
    UERANSIM_PRIVILEGED,
)

_tasks: Dict[str, dict] = {}


def create_task() -> str:
    task_id = str(uuid.uuid4())
    _tasks[task_id] = {
        "id": task_id,
        "status": "pending",
        "logs": [],
        "created": time.time(),
    }
    return task_id


def get_task(task_id: str) -> Optional[dict]:
    return _tasks.get(task_id)


async def run_test(task_id: str) -> None:
    task = _tasks[task_id]
    task["status"] = "running"

    def log(msg: str, type: str = "info") -> None:
        task["logs"].append({"msg": msg, "type": type, "ts": time.strftime("%H:%M:%S")})

    runtime_cmd = [CONTAINER_RUNTIME]
    if CONTAINER_HOST:
        runtime_cmd.extend(["--host", CONTAINER_HOST])

    log(f"▸ Checking UERANSIM image via {CONTAINER_RUNTIME}…", "run")
    check = await asyncio.create_subprocess_exec(
        *runtime_cmd, "images", "-q", UERANSIM_IMAGE,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )
    out, _ = await check.communicate()
    if not out.strip():
        log("✗ UERANSIM image not found.", "err")
        log("  Build it with `docker compose build ueransim` or set MARVIS_UERANSIM_IMAGE.", "err")
        task["status"] = "error"
        return

    log("  UERANSIM image ready", "ok")
    log("▸ Starting test container — allow up to 60s…", "run")

    try:
        run_cmd = [*runtime_cmd, "run", "--rm", "--name", UERANSIM_CONTAINER_NAME]
        if UERANSIM_NETWORK_MODE:
            run_cmd.append(f"--network={UERANSIM_NETWORK_MODE}")
        if UERANSIM_PRIVILEGED:
            run_cmd.append("--privileged")
        run_cmd.extend([
            "--env-file", UERANSIM_ENV_FILE,
            UERANSIM_IMAGE,
        ])

        proc = await asyncio.create_subprocess_exec(
            *run_cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT,
        )
        try:
            stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=90)
        except asyncio.TimeoutError:
            proc.kill()
            log("✗ Test timed out after 90s — container killed", "err")
            task["status"] = "error"
            return

        for line in stdout.decode(errors="replace").splitlines():
            line = line.strip()
            if not line:
                continue
            if "ERROR" in line:
                log(line, "err")
            elif "PASSED" in line or "established" in line or "successful" in line:
                log(line, "ok")
            elif "WARNING" in line:
                log(line, "warn")
            else:
                log(line, "info")

        if proc.returncode == 0:
            log("✓ Emulated data session completed successfully", "ok")
            task["status"] = "done"
        elif proc.returncode == 2:
            log(f"⚠ Credentials not configured — edit {UERANSIM_ENV_FILE}", "warn")
            task["status"] = "error"
        else:
            log(f"✗ Test exited with code {proc.returncode}", "err")
            task["status"] = "error"

    except Exception as exc:
        log(f"✗ Unexpected error: {exc}", "err")
        task["status"] = "error"
