import json
import logging
from datetime import datetime
from typing import Annotated, Any

from fastapi import APIRouter, HTTPException
from fastapi.params import Depends
from pydantic import BaseModel
from sqlalchemy.exc import NoResultFound
from sqlmodel import Session, select

from nyahome.config import config_manager
from nyahome.database import ModelUser, get_session
from nyahome.service.secure_service import SecureChange, s_append_secure_changes
from nyahome.service.verify_service import s_send_test_email, s_send_verify_email, s_verify_email

from .auth import create_access_token, save_password, verify_password, verify_token
from .response_model import ReturnDto

logger = logging.getLogger(__name__)

admin_router = APIRouter(tags=["Admin"], prefix="/admin")


class UserLogin(BaseModel):
    username: str
    password: str


class UserInfo(BaseModel):
    name: str
    display_name: str
    avatar_url: str
    background_url: str
    description: str


class ChangePassword(BaseModel):
    old_password: str
    new_password: str


class SendEmail(BaseModel):
    to: str


class VerifyEmail(BaseModel):
    to: str
    verify_code: str


@admin_router.post("/login/name/")
async def nyahome_login_name(user: UserLogin, session: Annotated[Session, Depends(get_session)]) -> ReturnDto:
    try:
        u: ModelUser = session.exec(select(ModelUser).where(ModelUser.name == user.username)).one()
    except NoResultFound:
        raise HTTPException(status_code=404, detail="用户不存在") from None

    if verify_password(user.password, u.password):
        change = SecureChange(
            created_at=datetime.now(),
            type="login",
            old=None,
            new=None,
        )
        u.secure_changes = s_append_secure_changes(u.secure_changes, change)
        session.add(u)
        session.commit()
        return ReturnDto(
            result={
                "user_id": u.id,
                "access_token": create_access_token(u.id, u.password, 30),
            }
        )
    raise HTTPException(status_code=401, detail="验证失败，请检查用户名和密码是否正确")


@admin_router.get("/me/")
async def nyahome_get_me(user: Annotated[ModelUser, Depends(verify_token)]) -> ModelUser:
    return user


@admin_router.post("/me/")
async def nyahome_post_me(
    info: UserInfo, user: Annotated[ModelUser, Depends(verify_token)], session: Annotated[Session, Depends(get_session)]
) -> ModelUser:
    user.name = info.name
    user.display_name = info.display_name
    user.avatar_url = info.avatar_url
    user.background_url = info.background_url
    user.description = info.description
    session.add(user)
    session.commit()
    session.refresh(user)
    return user


@admin_router.post("/me/password/")
async def nyahome_change_password(
    change: ChangePassword,
    user: Annotated[ModelUser, Depends(verify_token)],
    session: Annotated[Session, Depends(get_session)],
) -> ReturnDto:
    if verify_password(change.old_password, user.password):
        user.password = save_password(change.new_password)
        change_ = SecureChange(
            created_at=datetime.now(),
            type="change_password",
            old=None,
            new=None,
        )
        user.secure_changes = s_append_secure_changes(user.secure_changes, change_)
        session.add(user)
        session.commit()
        return ReturnDto(success=True)
    raise HTTPException(status_code=400, detail="修改密码需要提供旧的密码，但提供的旧密码错误。") from None


@admin_router.post("/me/email-verify/")
async def nyahome_verify_email(
    to: VerifyEmail,
    user: Annotated[ModelUser, Depends(verify_token)],
    session: Annotated[Session, Depends(get_session)],
) -> ReturnDto:
    success = await s_verify_email(user_id=user.id, address=to.to, verify_code=to.verify_code)
    if success:
        old_email = user.email
        user.email = to.to
        user.secure_changes = s_append_secure_changes(
            user.secure_changes,
            SecureChange(
                created_at=datetime.now(),
                type="change_email",
                old=old_email,
                new=to.to,
            ),
        )
        session.add(user)
        session.commit()
        logger.info(f"已更新用户 {user.id} 的邮件地址至 {user.email}")
    return ReturnDto(success=success)


@admin_router.post("/me/email-verify/send/")
async def nyahome_verify_email_send(to: SendEmail, user: Annotated[ModelUser, Depends(verify_token)]) -> ReturnDto:
    success = await s_send_verify_email(user.id, to.to)
    return ReturnDto(success=success)


@admin_router.get("/me/secure_changes/")
async def nyahome_get_secure_changes(
    user: Annotated[ModelUser, Depends(verify_token)],
) -> list[SecureChange]:
    return json.loads(user.secure_changes)  # type: ignore[no-any-return]


@admin_router.get("/site_config/")
async def get_site_config(user: Annotated[ModelUser, Depends(verify_token)]) -> dict[str, Any]:
    """
    获取 NyaHome 的设置。

    Raises:
        HTTPException: 403 表示请求用户非管理员。

    Returns:
        dict[str, Any] NyaHome 设置
    """
    if not user.is_admin:
        raise HTTPException(status_code=403, detail="非管理员禁止访问") from None
    return config_manager.get_config()


@admin_router.post("/site_config/")
async def set_site_config(
    user: Annotated[ModelUser, Depends(verify_token)],
    config_: dict[str, Any],
) -> dict[str, Any]:
    """
    设置 NyaHome 的设置。

    Raises:
        HTTPException: 403 表示请求用户非管理员。

    Returns:
        dict[str, Any] 更新过的 NyaHome 设置
    """
    if not user.is_admin:
        raise HTTPException(status_code=403, detail="非管理员禁止访问") from None
    final_config = config_manager.set_config(config_)
    await config_manager.async_save_config()
    return final_config


@admin_router.post("/email-test/")
async def nyahome_test_email(to: SendEmail, user: Annotated[ModelUser, Depends(verify_token)]) -> ReturnDto:
    """
    NyaHome 管理员面板中的测试邮件端点。

    Args:
        to: 测试邮件发送目标
        user: 当前用户，需要为管理员

    Raises:
        HTTPException: 403 表示请求用户非管理员。

    Returns:
        ReturnDto
    """
    if not user.is_admin:
        raise HTTPException(status_code=403, detail="非管理员禁止访问") from None
    success = await s_send_test_email(to.to)
    logger.info(f"发送测试邮件到 {to} - {success=}")
    return ReturnDto(success=success)
