from __future__ import annotations

import hmac
import time
from collections import defaultdict
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel, Field

from ..config import Settings, get_settings
from ..credentials import get_credentials, update_credentials, upsert_env_file
from .auth import LoginRequest, LoginResponse, issue_token, require_user
from .fleet import consume_login_ticket

router = APIRouter(prefix="/api/auth", tags=["auth"])


class FleetExchangeRequest(BaseModel):
    ticket: str = Field(min_length=8, max_length=256)

_login_hits: dict[str, list[float]] = defaultdict(list)


class ChangeCredentialsRequest(BaseModel):
    current_password: str = Field(min_length=1)
    new_username: str = Field(min_length=1, max_length=64)
    new_password: str = Field(min_length=8, max_length=128)


def _client_ip(request: Request) -> str:
    xff = request.headers.get("x-forwarded-for") or ""
    if xff.strip():
        return xff.split(",")[0].strip()
    if request.client:
        return request.client.host or "unknown"
    return "unknown"


def _rate_limit_login(ip: str, settings: Settings) -> None:
    now = time.time()
    window = float(settings.login_window_sec)
    max_n = int(settings.login_max_attempts)
    hits = [t for t in _login_hits[ip] if now - t < window]
    _login_hits[ip] = hits
    if len(hits) >= max_n:
        raise HTTPException(
            status_code=status.HTTP_429_TOO_MANY_REQUESTS,
            detail=f"登录过于频繁，请 {int(window)} 秒后再试",
        )


@router.post("/fleet-exchange", response_model=LoginResponse)
async def fleet_exchange(
    body: FleetExchangeRequest,
    settings: Annotated[Settings, Depends(get_settings)],
) -> LoginResponse:
    """中控签发的一次性 ticket 兑换为普通登录会话（免密）。"""
    username = consume_login_ticket(body.ticket)
    token, ttl = issue_token(username, settings)
    return LoginResponse(
        token=token,
        username=username,
        expires_in=ttl,
        env_name=settings.env_name,
        mode=settings.mode,
    )


@router.post("/login", response_model=LoginResponse)
async def login(
    body: LoginRequest,
    request: Request,
    settings: Annotated[Settings, Depends(get_settings)],
) -> LoginResponse:
    ip = _client_ip(request)
    _rate_limit_login(ip, settings)
    user, pwd = get_credentials()
    user_ok = hmac.compare_digest(
        body.username.encode("utf-8"), user.encode("utf-8")
    )
    pwd_ok = hmac.compare_digest(
        body.password.encode("utf-8"), pwd.encode("utf-8")
    )
    if not (user_ok and pwd_ok):
        _login_hits[ip].append(time.time())
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="用户名或密码错误"
        )
    _login_hits.pop(ip, None)
    token, ttl = issue_token(body.username, settings)
    return LoginResponse(
        token=token,
        username=body.username,
        expires_in=ttl,
        env_name=settings.env_name,
        mode=settings.mode,
    )


@router.get("/branding")
async def branding() -> dict:
    """登录页/顶栏展示用（无敏感信息）：与企微通知同一机器名。"""
    from ..notify import wecom

    return {"machine_name": wecom.wecom_machine_name() or ""}


@router.get("/me")
async def me(
    username: Annotated[str, Depends(require_user)],
    settings: Annotated[Settings, Depends(get_settings)],
) -> dict:
    from ..notify import wecom

    return {
        "username": username,
        "env_name": settings.env_name,
        "mode": settings.mode,
        "sim": settings.is_sim,
        "machine_name": wecom.wecom_machine_name() or "",
    }


@router.post("/refresh", response_model=LoginResponse)
async def refresh_token(
    username: Annotated[str, Depends(require_user)],
    settings: Annotated[Settings, Depends(get_settings)],
) -> LoginResponse:
    """用仍有效的 Bearer 换发新 HMAC token（自动轮换，无需重登）。"""
    token, ttl = issue_token(username, settings)
    return LoginResponse(
        token=token,
        username=username,
        expires_in=ttl,
        env_name=settings.env_name,
        mode=settings.mode,
    )


@router.post("/change-credentials", response_model=LoginResponse)
async def change_credentials(
    body: ChangeCredentialsRequest,
    username: Annotated[str, Depends(require_user)],
    settings: Annotated[Settings, Depends(get_settings)],
) -> LoginResponse:
    _cur_user, cur_pwd = get_credentials()
    if not hmac.compare_digest(
        body.current_password.encode("utf-8"), cur_pwd.encode("utf-8")
    ):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="当前密码不正确"
        )
    try:
        update_credentials(
            new_username=body.new_username, new_password=body.new_password
        )
        # 作废旧 token
        new_ver = int(settings.auth_token_version) + 1
        upsert_env_file("AUTH_TOKEN_VERSION", str(new_ver))
        get_settings.cache_clear()
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)
        ) from e
    settings2 = get_settings()
    token, ttl = issue_token(body.new_username.strip(), settings2)
    return LoginResponse(
        token=token,
        username=body.new_username.strip(),
        expires_in=ttl,
        env_name=settings2.env_name,
        mode=settings2.mode,
    )
