"""
LINE Messaging API webhook 端點(階段 6)。

LINE 把使用者 OA / 群組 / 1對1 的事件 POST 到這裡。我們:
  1. 用 X-Line-Signature 驗請求是不是真的來自 LINE(否則一律拒絕)
  2. 把每個 event 拋進背景 task,馬上回 200(LINE 規定要 5 秒內回)

routes:
  POST /webhook/line                      ─ LINE 推事件進來
  POST /api/line/push                     ─ 內部測試用:手動推一則文字到指定 to
  GET  /api/line/health                   ─ 設定狀態
"""

from __future__ import annotations

import asyncio
import json
import logging

from fastapi import APIRouter, Header, HTTPException, Request, status
from pydantic import BaseModel

from app.config import settings
from app.core.deps import CurrentUser, DBSession
from app.services import line_binding, line_bridge, line_client


logger = logging.getLogger("coba.line.webhook")
router = APIRouter(tags=["line"])


async def _process_line_webhook(
    request: Request,
    x_line_signature: str | None,
):
    body = await request.body()
    if not line_client.verify_signature(body, x_line_signature):
        # 401 而非 403:LINE 自家驗證工具會用空簽章送驗證 ping,我們一律拒
        logger.warning("LINE webhook 簽章驗證失敗")
        raise HTTPException(status_code=401, detail="invalid signature")

    try:
        payload = json.loads(body.decode("utf-8"))
    except Exception:
        raise HTTPException(status_code=400, detail="invalid json")

    events = payload.get("events", []) or []
    # 不 await:讓 5 秒 timeout 不卡到 LLM call
    for event in events:
        asyncio.create_task(line_bridge.handle_event(event))
    return {"ok": True, "events": len(events)}


@router.post("/webhook/line", status_code=status.HTTP_200_OK, summary="LINE Messaging API 入口(主)")
async def line_webhook(
    request: Request,
    x_line_signature: str | None = Header(default=None, alias="X-Line-Signature"),
):
    """LINE webhook 主入口。LINE 5 秒內要回 200。"""
    return await _process_line_webhook(request, x_line_signature)


@router.post("/api/line/webhook", status_code=status.HTTP_200_OK,
              summary="LINE webhook alias(舊 Console 設定)")
async def line_webhook_alias(
    request: Request,
    x_line_signature: str | None = Header(default=None, alias="X-Line-Signature"),
):
    """alias:舊版 LINE Developer Console 把 webhook URL 設成 /api/line/webhook,
    為避免 Robert 進 Console 改設定,server 兩條 path 都收。"""
    return await _process_line_webhook(request, x_line_signature)


# ============================================================
# 內部 / 管理用 endpoint(需登入)
# ============================================================


class _LinePushRequest(BaseModel):
    to: str   # userId / groupId / roomId
    text: str


@router.post("/api/line/push", summary="(管理員)主動推一則文字到 LINE")
async def manual_push(payload: _LinePushRequest, current_user: CurrentUser):
    """測試 / 緊急通知用。owner 才能呼叫。"""
    if current_user.role != "owner":
        raise HTTPException(status_code=403, detail="只有 owner 能用這個 endpoint")
    ok = await line_client.push_text(payload.to, payload.text)
    return {"ok": ok}


@router.get("/api/line/health", summary="LINE 整合健康檢查")
async def line_health():
    """看 LINE 的三個密鑰有沒有設好(不洩露具體值,只回 boolean)。"""
    return {
        "channel_id_set": bool(settings.LINE_CHANNEL_ID),
        "channel_secret_set": bool(settings.LINE_CHANNEL_SECRET),
        "access_token_set": bool(settings.LINE_CHANNEL_ACCESS_TOKEN),
    }


# ============================================================
# 帳號綁定:web 端產碼 + LINE 端輸入
# ============================================================


@router.post("/api/line/binding/code", summary="產生 LINE 綁定碼(10 分鐘有效)")
async def issue_binding_code(current_user: CurrentUser, db: DBSession):
    """登入後呼叫。回傳 6 位數綁定碼,使用者去 LINE 對 Cooper 講「綁定 482593」。

    注意:同一個 user 可重複呼叫,既有 unused/未過期碼仍有效。
    """
    rec = await line_binding.generate_code(db, current_user.id)
    await db.commit()
    return {
        "code": rec.code,
        "expires_at": rec.expires_at.isoformat(),
        "expires_in_seconds": int((rec.expires_at - rec.created_at).total_seconds()),
        "instruction": f"在 LINE 對 Cooper 傳「綁定 {rec.code}」即可完成綁定。",
    }


@router.get("/api/line/binding/status", summary="看自己當前 LINE 綁定狀態")
async def binding_status(current_user: CurrentUser):
    """回傳當前 user 是否已綁 LINE。"""
    return {
        "bound": bool(current_user.line_user_id),
        "line_user_id_partial": (current_user.line_user_id[:8] + "…") if current_user.line_user_id else None,
    }


@router.delete("/api/line/binding", summary="解除自己的 LINE 綁定")
async def unbind_line(current_user: CurrentUser, db: DBSession):
    """解綁後,自己的 Cooper 帳號 line_user_id 會被清掉。
    日後同一個 LINE userId 再傳訊息進來,會被當成新人,系統會自動建影子帳號。
    若想重新綁回原帳號,需要再走一次「產綁定碼 → LINE 傳碼」流程。
    """
    ok = await line_binding.unbind(db, current_user.id)
    await db.commit()
    return {"unbound": ok}
