"""
Magic Link 自動登入(階段 8 F)。

流程:
  1. Cooper 想給某個使用者「直接進工作台」連結時 → call generate_magic_link(user_id)
  2. 回 https://cooper-nas.taila49e28.ts.net/auth-from-line?t=<short JWT>
  3. 使用者手機點 → 外部瀏覽器打開
  4. 後端 /auth-from-line 驗 token → 發 access + refresh JWT → 回 HTML 把 token 寫入
     localStorage + redirect 到 / 主頁(已登入)

特性:
  - 短效:60 秒
  - 一次性:JTI 記在 in-memory set,用過就 invalid
  - 密鑰沿用 JWT_SECRET_KEY
"""

from __future__ import annotations

import logging
import time
import uuid as _uuid
from datetime import datetime, timedelta, timezone
from uuid import UUID

import jwt

from app.config import settings


logger = logging.getLogger("coba.magic_link")


MAGIC_TOKEN_TTL_SEC = 1800   # 30 分鐘 — 給人時間點 LINE 訊息再開連結
_USED_JTI: set[str] = set()


def generate_magic_link(user_id: UUID, base_url: str = "https://cooper-nas.taila49e28.ts.net") -> str:
    """產生 30 分鐘一次性的登入連結(進去後加到主畫面就免用 magic link)。"""
    now = datetime.now(timezone.utc)
    jti = _uuid.uuid4().hex
    payload = {
        "sub": str(user_id),
        "type": "magic",
        "jti": jti,
        "iat": int(now.timestamp()),
        "exp": int((now + timedelta(seconds=MAGIC_TOKEN_TTL_SEC)).timestamp()),
    }
    token = jwt.encode(payload, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
    return f"{base_url}/auth-from-line?t={token}"


def consume_magic_token(token: str) -> UUID:
    """驗 token + 標記用過。失敗 raise ValueError。"""
    try:
        payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])
    except jwt.ExpiredSignatureError:
        raise ValueError("連結已過期(60 秒一次性)")
    except jwt.InvalidTokenError:
        raise ValueError("連結無效")

    if payload.get("type") != "magic":
        raise ValueError("不是 magic 類型的連結")

    jti = payload.get("jti")
    if not jti:
        raise ValueError("缺 jti")
    if jti in _USED_JTI:
        raise ValueError("這個連結已經用過了")
    _USED_JTI.add(jti)
    # 簡單清理:set 太大時清掉一半(不需要精準)
    if len(_USED_JTI) > 5000:
        for j in list(_USED_JTI)[:2500]:
            _USED_JTI.discard(j)

    user_id_str = payload.get("sub")
    if not user_id_str:
        raise ValueError("token 沒帶 user_id")
    return UUID(user_id_str)
