from fastapi import FastAPI
from fastapi.responses import FileResponse, JSONResponse
from pathlib import Path
import asyncio
import re
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI(title="HDMI 录制控制台")

# ---------------- 配置 ----------------
BOT_NAME = "hdmi"
BASE_DIR = Path(__file__).parent
DEFAULT_LOG_DIR = BASE_DIR.parent / ".pm2" / "logs"
MAX_LOG_LINES = 300

# CORS
app.add_middleware(
   CORSMiddleware,
   allow_origins=["*"],
   allow_methods=["GET"],
   allow_headers=["*"],
)

# ---------------- 工具 ----------------
def strip_ansi_codes(text: str) -> str:
   return re.sub(r'\x1b\[[0-9;]*m', '', text)

async def run_pm2(cmd: str) -> str:
   process = await asyncio.create_subprocess_shell(
       f"pm2 {cmd}",
       stdout=asyncio.subprocess.PIPE,
       stderr=asyncio.subprocess.PIPE,
   )
   try:
       stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=15.0)
       return (stdout or stderr).decode(errors="ignore").strip()
   except asyncio.TimeoutError:
       process.kill()
       return "ERROR: PM2 命令超时"

async def read_log_path(path: str, lines: int) -> str:
   if not path:
       return "无日志路径\n"
   if "/dev/null" in path:
       return "日志输出被禁用（PM2 配置为无日志）\n"
   log_file = Path(path)
   if not log_file.exists():
       return f"日志文件不存在: {path}\n"
   try:
       with open(log_file, "r", encoding="utf-8", errors="ignore") as f:
           content_lines = f.readlines()
           return "".join(content_lines[-lines:]) if content_lines else "无日志内容\n"
   except Exception as e:
       return f"读取日志失败 ({path}): {str(e)}\n"

async def get_hdmi_info() -> dict:
   describe_raw = await run_pm2(f"describe {BOT_NAME}")
   describe_clean = strip_ansi_codes(describe_raw)

   if ("No such process" in describe_clean or
       "Unknown process" in describe_clean or
       "not found" in describe_clean.lower() or
       not describe_clean.strip()):
       return {
           "process_status": "not_found",
           "out_path": None,
           "err_path": None,
       }

   status = "unknown"
   out_path = None
   err_path = None

   for line in describe_raw.splitlines():
       line_clean = strip_ansi_codes(line)
       if line_clean.count('│') < 2:
           continue
       parts = [p.strip() for p in line_clean.split('│') if p.strip()]

       if len(parts) >= 2:
           key = parts[0].lower()
           value = parts[1]

           if key == "status":
               status = value.lower()
           elif key == "out_log_path":
               out_path = value
           elif key == "err_log_path":
               err_path = value

   # 备用默认路径（仅当 describe 未提供时）
   if not out_path:
       out_path = str(DEFAULT_LOG_DIR / f"{BOT_NAME}-out.log")
   if not err_path:
       err_path = str(DEFAULT_LOG_DIR / f"{BOT_NAME}-error.log")

   return {
       "process_status": status,
       "out_path": out_path,
       "err_path": err_path,
   }

# ---------------- 路由 ----------------
@app.get("/start")
async def start():
   output = await run_pm2(f"start {BOT_NAME}")
   return JSONResponse({"output": output})

@app.get("/stop")
async def stop():
   output = await run_pm2(f"stop {BOT_NAME}")
   return JSONResponse({"output": output})

@app.get("/restart")
async def restart():
   output = await run_pm2(f"restart {BOT_NAME}")
   return JSONResponse({"output": output})

@app.get("/status")
async def status():
   full_status_raw = await run_pm2("status")
   full_status = strip_ansi_codes(full_status_raw) # 去除颜色代码，前端显示更干净

   info = await get_hdmi_info()

   recent_log = await read_log_path(info["out_path"], MAX_LOG_LINES)
   recent_error = await read_log_path(info["err_path"], MAX_LOG_LINES)

   if info["process_status"] == "not_found":
       recent_log = "进程未注册到 PM2（可能被 pm2 delete 删除或从未保存）\n"
       recent_error = ""

   return JSONResponse({
       "raw_status": full_status,
       "process_status": info["process_status"],
       "recent_log": recent_log,
       "recent_error": recent_error,
   })

@app.get("/")
async def index():
   return FileResponse(BASE_DIR / "index.html")
