import datetime
import hashlib

# import logging
from typing import Annotated, Any

from fastapi import Depends, HTTPException
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import jwt
from sqlalchemy.exc import NoResultFound
from sqlmodel import Session, select

from nyahome.config import config_manager
from nyahome.core.password import pwd_context
from nyahome.database import ModelUser, get_session

# logger = logging.getLogger(__name__)

security = HTTPBearer()


def create_access_token(user_id: int, user_password: str, expire: int) -> str:
    """
    签发一个 access Token 给指定用户。

    Args:
        user_id: 用户 ID
        user_password: 用户经过加密的密码密文
        expire: 逾期时间，单位为天

    Returns:
        签发得到的 JWT Token
    """
    return jwt.encode(
        {
            "user_id": user_id,
            "pw_hash": hashlib.sha256(user_password.encode("utf-8")).hexdigest(),
            "exp": datetime.datetime.now() + datetime.timedelta(days=expire),
        },
        config_manager.get("site_jwt_secret", "see you tomorrow"),
        algorithm="HS256",
    )


def verify_access_token(token: str, user_id: int | None = None) -> dict[str, Any]:
    try:
        claims = jwt.decode(token, config_manager.get("site_jwt_secret", "see you tomorrow"))
    except Exception as e:
        # logger.info(f"验证一个 Access Token 失败：{user_id=} | {e}")
        raise ValueError("验证 Access Token 失败") from e
    # 如果提供了 user_id 则顺手进行检查
    if user_id and claims.get("user_id") != user_id:
        # logger.info(f"验证一个 Access Token 失败：{user_id=} | Token 有效，但用户错误。")
        raise NameError("正在检查的 Access Token 不是签发给提供用户的……")
    # logger.info(f"验证一个 Access Token 成功：{user_id=}")
    return claims


def verify_password(input_password: str, saved_password: str) -> bool:
    """
    验证用户登录请求的密码是否正确。

    Args:
        input_password: 前端直接提供的密码原文
        saved_password: 保存在数据库中的、经过加密的密码

    Returns:
        布尔值表明正确与否
    """
    return pwd_context.verify(input_password, saved_password)


def save_password(password: str) -> str:
    return pwd_context.hash(password)


async def verify_token(
    credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)],
    session: Annotated[Session, Depends(get_session)],
) -> ModelUser:
    """
    验证 Bearer Token。

    验证内容包括 Access Token 本身合法性以及签发的目标用户合法性。

    另外，修改密码会导致所有签发的 Access Token 失效。

    Raises:
        HTTPException: 所有验证失败均返回 401。

    Returns:
        ModelUser
    """
    token = credentials.credentials

    try:
        claims = verify_access_token(token)
    except Exception as e:
        raise HTTPException(status_code=401, detail="Access Token 验证失败1") from e

    user_id = claims.get("user_id")
    try:
        user: ModelUser = session.exec(select(ModelUser).where(ModelUser.id == user_id)).one()
    except NoResultFound:
        raise HTTPException(status_code=401, detail="Access Token 验证失败2") from None
    if hashlib.sha256(user.password.encode("utf-8")).hexdigest() != claims.get("pw_hash"):
        raise HTTPException(status_code=401, detail="Access Token 验证失败3") from None

    return user
