"""YakPanel - Node.js / PM2 API"""
import json
import re
import time
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel

from app.core.utils import exec_shell_sync
from app.api.auth import get_current_user
from app.models.user import User

router = APIRouter(prefix="/node", tags=["node"])


class AddProcessRequest(BaseModel):
    script: str
    name: str = ""


@router.get("/processes")
async def node_processes(current_user: User = Depends(get_current_user)):
    """List PM2 processes (pm2 jlist)"""
    out, err = exec_shell_sync("pm2 jlist", timeout=10)
    if err and "not found" in err.lower():
        return {"processes": [], "error": "PM2 not installed"}
    try:
        data = json.loads(out) if out.strip() else []
        processes = []
        now_ms = int(time.time() * 1000)
        for p in data if isinstance(data, list) else []:
            name = p.get("name", "")
            pm2_env = p.get("pm2_env", {})
            start_ms = pm2_env.get("pm_uptime", 0)
            uptime_ms = (now_ms - start_ms) if start_ms and pm2_env.get("status") == "online" else 0
            processes.append({
                "id": p.get("pm_id"),
                "name": name,
                "status": pm2_env.get("status", "unknown"),
                "pid": p.get("pid"),
                "uptime": uptime_ms,
                "restarts": pm2_env.get("restart_time", 0),
                "memory": p.get("monit", {}).get("memory", 0),
                "cpu": p.get("monit", {}).get("cpu", 0),
            })
        return {"processes": processes}
    except json.JSONDecodeError:
        return {"processes": [], "error": "Failed to parse PM2 output"}


@router.post("/add")
async def node_add(
    body: AddProcessRequest,
    current_user: User = Depends(get_current_user),
):
    """Add and start a new PM2 process (pm2 start script --name name)"""
    script = (body.script or "").strip()
    if not script:
        raise HTTPException(status_code=400, detail="Script path required")
    if ".." in script or ";" in script or "|" in script or "`" in script:
        raise HTTPException(status_code=400, detail="Invalid script path")
    name = (body.name or "").strip()
    if name and ("'" in name or '"' in name or ";" in name):
        raise HTTPException(status_code=400, detail="Invalid process name")
    cmd = f"pm2 start {script}"
    if name:
        cmd += f" --name '{name}'"
    out, err = exec_shell_sync(cmd, timeout=15)
    if err and "error" in err.lower():
        raise HTTPException(status_code=500, detail=err.strip() or out.strip())
    return {"status": True, "msg": "Process started"}


@router.get("/version")
async def node_version(current_user: User = Depends(get_current_user)):
    """Get Node.js version"""
    out, err = exec_shell_sync("node -v", timeout=5)
    version = out.strip() if out else ""
    if err and "not found" in err.lower():
        return {"version": None, "error": "Node.js not installed"}
    return {"version": version or None}


@router.post("/{proc_id}/start")
async def node_start(
    proc_id: str,
    current_user: User = Depends(get_current_user),
):
    """Start PM2 process"""
    if not re.match(r"^\d+$", proc_id):
        raise HTTPException(status_code=400, detail="Invalid process ID")
    out, err = exec_shell_sync(f"pm2 start {proc_id}", timeout=15)
    if err and "error" in err.lower():
        raise HTTPException(status_code=500, detail=err.strip() or out.strip())
    return {"status": True, "msg": "Started"}


@router.post("/{proc_id}/stop")
async def node_stop(
    proc_id: str,
    current_user: User = Depends(get_current_user),
):
    """Stop PM2 process"""
    if not re.match(r"^\d+$", proc_id):
        raise HTTPException(status_code=400, detail="Invalid process ID")
    out, err = exec_shell_sync(f"pm2 stop {proc_id}", timeout=15)
    if err and "error" in err.lower():
        raise HTTPException(status_code=500, detail=err.strip() or out.strip())
    return {"status": True, "msg": "Stopped"}


@router.post("/{proc_id}/restart")
async def node_restart(
    proc_id: str,
    current_user: User = Depends(get_current_user),
):
    """Restart PM2 process"""
    if not re.match(r"^\d+$", proc_id):
        raise HTTPException(status_code=400, detail="Invalid process ID")
    out, err = exec_shell_sync(f"pm2 restart {proc_id}", timeout=15)
    if err and "error" in err.lower():
        raise HTTPException(status_code=500, detail=err.strip() or out.strip())
    return {"status": True, "msg": "Restarted"}


@router.delete("/{proc_id}")
async def node_delete(
    proc_id: str,
    current_user: User = Depends(get_current_user),
):
    """Delete PM2 process"""
    if not re.match(r"^\d+$", proc_id):
        raise HTTPException(status_code=400, detail="Invalid process ID")
    out, err = exec_shell_sync(f"pm2 delete {proc_id}", timeout=15)
    if err and "error" in err.lower():
        raise HTTPException(status_code=500, detail=err.strip() or out.strip())
    return {"status": True, "msg": "Deleted"}
