# --- routers/lock.py ---
import time
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import Dict, Optional

router = APIRouter()

# ==========================================
# 💡 全域鎖定表 (In-Memory Lock Table)
# 結構: { "path": {"user_id": "...", "username": "...", "expires_at": 1234567890.123} }
# ==========================================
ACTIVE_LOCKS: Dict[str, dict] = {}

# 鎖定超時時間 (秒)：前端需在此時間內發送 Heartbeat，否則自動釋放
LOCK_TIMEOUT = 30  

class LockAcquireReq(BaseModel):
    path: str
    user_id: str      # 前端產生的 UUID (Session ID)
    username: str     # 登入的帳號 (用於 UI 提示，例如 "admin")

class LockActionReq(BaseModel):
    path: str
    user_id: str

def clean_expired_locks():
    """清除已經超時的鎖 (被動式清理)"""
    current_time = time.time()
    expired_paths = [path for path, lock in ACTIVE_LOCKS.items() if lock["expires_at"] < current_time]
    for path in expired_paths:
        del ACTIVE_LOCKS[path]

def is_path_locked_by_others(target_path: str, user_id: str) -> tuple[Optional[dict], str]:
    """
    檢查路徑是否被其他人鎖定，並回傳 (衝突的鎖定資訊, 具體錯誤訊息)
    """
    clean_expired_locks()
    
    for locked_path, lock_info in ACTIVE_LOCKS.items():
        if lock_info["user_id"] == user_id:
            continue # 自己鎖定的不算衝突
            
        # 💡 拆分判斷，回傳更精準的錯誤訊息
        if target_path == locked_path:
            return lock_info, f"此區塊正由 [{lock_info['username']}] 編輯中"
            
        if target_path.startswith(locked_path + "."):
            return lock_info, f"父層級 [{locked_path}] 已被 [{lock_info['username']}] 鎖定，無法編輯子區塊"
            
        if locked_path.startswith(target_path + "."):
            return lock_info, f"子層級 [{locked_path}] 已被 [{lock_info['username']}] 鎖定，無法鎖定整個父區塊"
            
    return None, ""

# ==========================================
# 💡 API Endpoints
# ==========================================

@router.post("/acquire")
async def acquire_lock(req: LockAcquireReq):
    """請求鎖定特定路徑"""
    # 接收回傳的鎖定資訊與具體訊息
    conflict_lock, error_msg = is_path_locked_by_others(req.path, req.user_id)
    
    if conflict_lock:
        raise HTTPException(
            status_code=409, 
            detail=error_msg  # 💡 將精準的錯誤訊息傳給前端
        )
        
    # 寫入或更新鎖定狀態
    ACTIVE_LOCKS[req.path] = {
        "user_id": req.user_id,
        "username": req.username,
        "expires_at": time.time() + LOCK_TIMEOUT
    }
    return {"status": "success", "message": f"成功鎖定路徑: {req.path}"}

@router.post("/heartbeat")
async def heartbeat_lock(req: LockActionReq):
    """延長鎖定時間 (心跳偵測)"""
    clean_expired_locks()
    
    if req.path not in ACTIVE_LOCKS:
        raise HTTPException(status_code=404, detail="鎖定已失效，請重新獲取")
        
    if ACTIVE_LOCKS[req.path]["user_id"] != req.user_id:
        raise HTTPException(status_code=403, detail="無權更新他人的鎖定")
        
    # 延長鎖定時間
    ACTIVE_LOCKS[req.path]["expires_at"] = time.time() + LOCK_TIMEOUT
    return {"status": "success", "message": "心跳更新成功"}

@router.post("/release")
async def release_lock(req: LockActionReq):
    """主動釋放鎖定"""
    if req.path in ACTIVE_LOCKS and ACTIVE_LOCKS[req.path]["user_id"] == req.user_id:
        del ACTIVE_LOCKS[req.path]
    return {"status": "success", "message": "鎖定已釋放"}

@router.get("/status")
async def get_lock_status():
    """獲取目前所有被鎖定的路徑 (供前端 UI 標示 '編輯中' 狀態)"""
    clean_expired_locks()
    return {"status": "success", "data": ACTIVE_LOCKS}
