"""GUARDiA CI/CD 자동 배포 — workspace → Gitea → 서버 배포 트리거."""
from __future__ import annotations
import asyncio, logging, subprocess
from datetime import datetime
from pathlib import Path
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select, desc
from sqlalchemy.ext.asyncio import AsyncSession
from core.auth import get_current_user, require_admin_role
from database import get_db
from models import User

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/cicd", tags=["CI/CD 자동배포"])

MONOREPO   = Path("/opt/guardia")          # 서버 소스 루트
GITEA_BASE = "https://zio:Zio%40Admin2026%21@zioinfo.co.kr:3000/zio"

REPO_MAP = {
    "guardia-itsm":     {"src": "/opt/guardia/workspace/guardia-itsm",     "service": "guardia"},
    "guardia-manager":  {"src": "/opt/guardia/workspace/guardia-manager",   "service": "guardia-manager"},
    "zioinfo-web":      {"src": "/opt/zioinfo/workspace/zioinfo-web",       "service": "zioinfo"},
    "guardia-docs":     {"src": "/opt/guardia/workspace/guardia-docs",      "service": None},
}

# 배포 이력 (메모리 — 재시작 시 초기화)
_deploy_history: list[dict] = []


def _run(cmd: str | list, timeout: int = 120) -> tuple[bool, str]:
    try:
        r = subprocess.run(
            cmd, shell=isinstance(cmd, str),
            capture_output=True, text=True, timeout=timeout
        )
        return r.returncode == 0, (r.stdout + r.stderr)[:500]
    except Exception as e:
        return False, str(e)


async def _deploy_repo(repo_name: str, triggered_by: str = "manual"):
    """workspace → Gitea push → webhook 트리거."""
    entry = {
        "repo": repo_name, "triggered_by": triggered_by,
        "started_at": datetime.utcnow().isoformat(),
        "status": "running", "steps": []
    }
    _deploy_history.insert(0, entry)
    if len(_deploy_history) > 50:
        _deploy_history.pop()

    def step(name: str, cmd):
        ok, out = _run(cmd)
        entry["steps"].append({"name": name, "ok": ok, "out": out[:200]})
        logger.info("[cicd:%s] %s → %s", repo_name, name, "OK" if ok else "FAIL")
        return ok

    info = REPO_MAP.get(repo_name)
    if not info:
        entry["status"] = "error"
        entry["steps"].append({"name": "validate", "ok": False, "out": "알 수 없는 repo"})
        return

    src = info["src"]
    repos_dir = f"/opt/guardia/repos/{repo_name}"

    try:
        # 1. repos/ clone (없는 경우)
        import os
        if not os.path.isdir(f"{repos_dir}/.git"):
            ok = step("clone", f"git clone {GITEA_BASE}/{repo_name}.git {repos_dir}")
            if not ok:
                entry["status"] = "failed"; return

        # 2. workspace → repos/ 동기화
        ok = step("sync", (
            f"rsync -a --delete "
            f"--exclude='.git' --exclude='__pycache__' --exclude='*.pyc' "
            f"--exclude='node_modules' --exclude='.env' --exclude='dist/' "
            f"{src}/ {repos_dir}/"
        ))
        if not ok:
            entry["status"] = "failed"; return

        # 3. git commit + push → Gitea webhook 트리거
        ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M")
        ok = step("commit", f"bash -c 'cd {repos_dir} && git add -A && (git diff --cached --quiet || git commit -m \"auto-deploy {ts}\")'")
        ok2 = step("push", f"bash -c 'cd {repos_dir} && git remote set-url origin {GITEA_BASE}/{repo_name}.git && git push origin main'")

        entry["status"] = "success" if (ok and ok2) else "partial"

        # 4. 메신저 알림
        try:
            import httpx
            async with httpx.AsyncClient(timeout=5) as c:
                await c.post("http://127.0.0.1:9001/api/messenger/webhook", json={
                    "event": "cicd_deploy",
                    "room": "ops",
                    "message": f"🚀 {repo_name} 배포 {'완료' if ok2 else '실패'} (by {triggered_by})",
                })
        except Exception:
            pass

    except Exception as e:
        entry["status"] = "error"
        entry["steps"].append({"name": "exception", "ok": False, "out": str(e)})

    entry["finished_at"] = datetime.utcnow().isoformat()


class DeployRequest(BaseModel):
    repo: str
    note: str = ""


@router.post("/deploy")
async def deploy(
    req: DeployRequest,
    background_tasks: BackgroundTasks,
    user: User = Depends(require_admin_role),
):
    """수동 배포 트리거."""
    if req.repo not in REPO_MAP and req.repo != "all":
        raise HTTPException(400, detail=f"지원 repo: {list(REPO_MAP)} 또는 'all'")
    repos = list(REPO_MAP) if req.repo == "all" else [req.repo]
    for r in repos:
        background_tasks.add_task(_deploy_repo, r, user.username)
    return {"ok": True, "repos": repos, "message": "배포 시작됨 (백그라운드)"}


@router.get("/status")
async def deploy_status(user: User = Depends(get_current_user)):
    """최근 배포 상태 (최대 20건)."""
    return {"history": _deploy_history[:20]}


@router.get("/repos")
async def list_repos(user: User = Depends(get_current_user)):
    """배포 가능한 repo 목록."""
    return {"repos": list(REPO_MAP.keys())}


@router.post("/check-and-deploy")
async def check_and_deploy(
    background_tasks: BackgroundTasks,
    user: User = Depends(require_admin_role),
):
    """workspace ↔ Gitea 차이 감지 → 변경된 repo만 배포."""
    background_tasks.add_task(_check_and_deploy_all, user.username)
    return {"ok": True, "message": "변경 감지 + 자동 배포 시작됨 (백그라운드)"}


async def _check_and_deploy_all(triggered_by: str = "schedule"):
    """모든 repo 변경 감지 → 배포."""
    for repo_name in REPO_MAP:
        src = REPO_MAP[repo_name]["src"]
        repos_dir = f"/opt/guardia/repos/{repo_name}"
        try:
            import os, hashlib
            if not os.path.isdir(src):
                continue
            # workspace와 repos/ 사이 diff 체크 (간단히 파일 수/mtime 비교)
            src_count  = sum(1 for _ in Path(src).rglob("*") if _.is_file() and ".git" not in str(_))
            dest_count = sum(1 for _ in Path(repos_dir).rglob("*") if _.is_file() and ".git" not in str(_)) if os.path.isdir(repos_dir) else 0
            if abs(src_count - dest_count) > 0:
                logger.info("[cicd:check] %s 변경 감지 — 배포 시작", repo_name)
                await _deploy_repo(repo_name, triggered_by)
        except Exception as e:
            logger.warning("[cicd:check] %s 오류: %s", repo_name, e)
