from __future__ import annotations

import hmac
from typing import Annotated

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

from ..auth import issue_token, require_control_user
from ..config import ControlSettings, get_control_settings
from ..envfile import update_control_credentials, upsert_env_control
from ..lan import client_ip, is_lan_ip

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


class LoginBody(BaseModel):
    username: str = Field(min_length=1)
    password: str = Field(min_length=1)


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


class LanBypassBody(BaseModel):
    enabled: bool


@router.get("/login-meta")
async def login_meta(
    request: Request,
    settings: Annotated[ControlSettings, Depends(get_control_settings)],
) -> dict:
    ip = client_ip(request)
    lan = is_lan_ip(ip)
    return {
        "show_default_hint": settings.is_default_credentials,
        "lan_bypass_enabled": settings.lan_auth_bypass,
        "lan_client": lan,
        "lan_login_available": bool(settings.lan_auth_bypass and lan),
        "client_ip": ip or None,
    }


@router.post("/lan-login")
async def lan_login(
    request: Request,
    settings: Annotated[ControlSettings, Depends(get_control_settings)],
) -> dict:
    if not settings.lan_auth_bypass:
        raise HTTPException(status_code=403, detail="未开启局域网免登录")
    ip = client_ip(request)
    if not is_lan_ip(ip):
        raise HTTPException(status_code=403, detail="仅局域网地址可免登录")
    user = settings.control_auth_username
    token, ttl = issue_token(user, settings)
    return {
        "token": token,
        "username": user,
        "expires_in": ttl,
        "via": "lan",
    }


@router.post("/login")
async def login(
    body: LoginBody,
    settings: Annotated[ControlSettings, Depends(get_control_settings)],
) -> dict:
    user_ok = hmac.compare_digest(
        body.username.encode("utf-8"),
        settings.control_auth_username.encode("utf-8"),
    )
    pwd_ok = hmac.compare_digest(
        body.password.encode("utf-8"),
        settings.control_auth_password.encode("utf-8"),
    )
    if not (user_ok and pwd_ok):
        raise HTTPException(status_code=401, detail="用户名或密码错误")
    token, ttl = issue_token(body.username, settings)
    return {"token": token, "username": body.username, "expires_in": ttl}


@router.get("/me")
async def me(
    request: Request,
    username: Annotated[str, Depends(require_control_user)],
    settings: Annotated[ControlSettings, Depends(get_control_settings)],
) -> dict:
    ip = client_ip(request)
    lan = is_lan_ip(ip)
    return {
        "username": username,
        "poll_interval_sec": settings.control_poll_interval_sec,
        "sse_interval_sec": settings.control_sse_interval_sec,
        "show_default_hint": settings.is_default_credentials,
        "lan_bypass_enabled": settings.lan_auth_bypass,
        "lan_client": lan,
        "client_ip": ip or None,
    }


@router.put("/lan-bypass")
async def put_lan_bypass(
    body: LanBypassBody,
    _user: Annotated[str, Depends(require_control_user)],
) -> dict:
    upsert_env_control(
        "CONTROL_LAN_AUTH_BYPASS",
        "1" if body.enabled else "0",
        overwrite=True,
    )
    get_control_settings.cache_clear()
    settings = get_control_settings()
    return {
        "ok": True,
        "lan_bypass_enabled": settings.lan_auth_bypass,
    }


@router.post("/change-credentials")
async def change_credentials(
    body: ChangeCredentialsBody,
    username: Annotated[str, Depends(require_control_user)],
    settings: Annotated[ControlSettings, Depends(get_control_settings)],
) -> dict:
    if not hmac.compare_digest(
        body.current_password.encode("utf-8"),
        settings.control_auth_password.encode("utf-8"),
    ):
        raise HTTPException(status_code=400, detail="当前密码不正确")
    try:
        update_control_credentials(
            new_username=body.new_username,
            new_password=body.new_password,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e)) from e
    settings2 = get_control_settings()
    token, ttl = issue_token(body.new_username.strip(), settings2)
    return {
        "token": token,
        "username": body.new_username.strip(),
        "expires_in": ttl,
        "show_default_hint": settings2.is_default_credentials,
    }
