"""
Canonical authentication API routes.

The route layer is intentionally thin: repository construction lives in
app.dependencies and authentication behavior lives in application use cases.
"""

import logging
import os
from typing import Optional

import jwt
from app.auth import AuthenticatedUser, blacklist_token, get_current_user
from app.config import settings
from app.dependencies import get_auth_email_service, get_auth_session_store, get_user_repository
from fastapi import APIRouter, Depends, Header, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from pydantic import BaseModel, EmailStr

from packages.adapters.redis import NoopSessionStore
from packages.adapters.smtp import NoopEmailService
from packages.application.auth.login_use_case import LoginRequest as LoginUseCaseRequest
from packages.application.auth.login_use_case import LoginUseCase
from packages.application.auth.login_use_case import RefreshTokenRequest as RefreshTokenUseCaseRequest
from packages.application.auth.login_use_case import RefreshTokenUseCase
from packages.application.auth.password_reset_use_case import RequestPasswordResetRequest as PasswordResetUseCaseRequest
from packages.application.auth.password_reset_use_case import (
    RequestPasswordResetUseCase,
    ResetPasswordRequest,
    ResetPasswordUseCase,
)
from packages.application.auth.register_user_use_case import RegisterUserRequest as RegisterUseCaseRequest
from packages.application.auth.register_user_use_case import RegisterUserUseCase, VerifyEmailRequest, VerifyEmailUseCase
from packages.ports.user_repository import UserRepository

logger = logging.getLogger(__name__)

bearer_scheme = HTTPBearer(auto_error=False)

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


class RegisterRequest(BaseModel):
    email: EmailStr
    password: str
    username: str
    display_name: Optional[str] = None


class RegisterResponse(BaseModel):
    user_id: str
    email: str
    username: str
    display_name: str
    message: str


class LoginRequest(BaseModel):
    email: EmailStr
    password: str


class RefreshRequest(BaseModel):
    refresh_token: str


class LoginResponse(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str = "bearer"
    user_id: str
    email: str
    username: str
    display_name: str
    expires_in: int


class CurrentUserResponse(BaseModel):
    user_id: str
    email: str
    username: str
    display_name: str
    email_verified: bool


class PasswordResetRequestModel(BaseModel):
    email: EmailStr


class ResetPasswordModel(BaseModel):
    token: str
    new_password: str


class VerifyEmailRequestModel(BaseModel):
    token: str


class MessageResponse(BaseModel):
    message: str


@router.post("/register", response_model=RegisterResponse, status_code=status.HTTP_201_CREATED)
async def register(
    request: RegisterRequest,
    user_repository: UserRepository = Depends(get_user_repository),
    email_service=Depends(get_auth_email_service),
):
    use_case = RegisterUserUseCase(
        user_repository=user_repository,
        base_url=settings.APP_BASE_URL,
        email_service=email_service,
    )
    response, error = use_case.execute(
        RegisterUseCaseRequest(
            email=request.email,
            password=request.password,
            username=request.username,
            display_name=request.display_name or request.username,
        )
    )
    if error or response is None:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=_translate_auth_error(error))

    return RegisterResponse(
        user_id=response.user_id,
        email=response.email,
        username=response.username,
        display_name=response.display_name,
        message="注册成功！",
    )


@router.post("/login", response_model=LoginResponse)
async def login(
    request: LoginRequest,
    user_repository: UserRepository = Depends(get_user_repository),
    session_store=Depends(get_auth_session_store),
):
    use_case = LoginUseCase(
        user_repository=user_repository,
        session_store=session_store,
        jwt_secret_key=settings.JWT_SECRET_KEY,
    )
    response, error = use_case.execute(LoginUseCaseRequest(email=request.email, password=request.password))
    if error or response is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="邮箱或密码错误")

    return LoginResponse(
        access_token=response.access_token,
        refresh_token=response.refresh_token,
        user_id=response.user_id,
        email=response.email,
        username=response.username,
        display_name=response.display_name,
        expires_in=response.expires_in,
    )


@router.post("/refresh")
async def refresh(
    request: RefreshRequest,
    user_repository: UserRepository = Depends(get_user_repository),
    session_store=Depends(get_auth_session_store),
):
    use_case = RefreshTokenUseCase(
        user_repository=user_repository,
        session_store=session_store,
    )
    response, error = use_case.execute(RefreshTokenUseCaseRequest(refresh_token=request.refresh_token))
    if error or response is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired refresh token")

    return LoginResponse(
        access_token=response.access_token,
        refresh_token=response.refresh_token,
        user_id=response.user_id,
        email=response.email,
        username=response.username,
        display_name=response.display_name,
        expires_in=response.expires_in,
    )


def _verify_email_token(token: str, user_repository: UserRepository) -> MessageResponse:
    success, error = VerifyEmailUseCase(user_repository=user_repository).execute(VerifyEmailRequest(token=token))
    if not success:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=error or "邮箱验证失败")

    return MessageResponse(message="邮箱验证成功")


@router.get("/verify-email", response_model=MessageResponse)
async def verify_email(
    token: str,
    user_repository: UserRepository = Depends(get_user_repository),
):
    return _verify_email_token(token, user_repository)


@router.post("/verify-email", response_model=MessageResponse)
async def verify_email_post(
    request: VerifyEmailRequestModel,
    user_repository: UserRepository = Depends(get_user_repository),
):
    return _verify_email_token(request.token, user_repository)


@router.post("/password/forgot", response_model=MessageResponse, status_code=status.HTTP_202_ACCEPTED)
async def forgot_password(
    request: PasswordResetRequestModel,
    user_repository: UserRepository = Depends(get_user_repository),
    email_service=Depends(get_auth_email_service),
):
    success, error = RequestPasswordResetUseCase(
        user_repository=user_repository,
        base_url=settings.APP_BASE_URL,
        email_service=email_service,
    ).execute(PasswordResetUseCaseRequest(email=request.email))
    if not success:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=error or "密码重置请求失败")

    return MessageResponse(message="如果账户存在，密码重置邮件已发送")


@router.post("/password/reset", response_model=MessageResponse)
async def reset_password(
    request: ResetPasswordModel,
    user_repository: UserRepository = Depends(get_user_repository),
):
    success, error = ResetPasswordUseCase(user_repository=user_repository).execute(
        ResetPasswordRequest(token=request.token, new_password=request.new_password)
    )
    if not success:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=error or "密码重置失败")

    return MessageResponse(message="密码重置成功")


@router.post("/logout")
async def logout(
    credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
    current_user: AuthenticatedUser = Depends(get_current_user),
):
    """登出 - 将当前 token 加入黑名单"""
    from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

    if credentials:
        try:
            payload = jwt.decode(credentials.credentials, settings.JWT_SECRET_KEY, algorithms=["HS256"])
            exp = payload.get("exp", 0)
            blacklist_token(credentials.credentials, exp)
        except Exception as e:
            logger.warning(f"Operation failed in apps/api/app/api/routes/auth.py: {e}", exc_info=True)
    return MessageResponse(message="已登出")


@router.get("/me", response_model=CurrentUserResponse)
async def get_current_user_info(
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
):
    user = authenticated_user.user
    return CurrentUserResponse(
        user_id=user.id,
        email=user.email,
        username=user.username,
        display_name=user.display_name,
        email_verified=user.email_verified,
    )


class _NoopSessionStore(NoopSessionStore):
    pass


class _NoopEmailService(NoopEmailService):
    pass


def _translate_auth_error(error: str | None) -> str:
    translations = {
        "Email already registered": "邮箱已被注册",
        "Username already taken": "用户名已被使用",
        "Username is required": "用户名不能为空",
        "Display name is required": "显示名称不能为空",
    }
    return translations.get(error or "", error or "注册失败")


class WechatSyncRequest(BaseModel):
    openid: str
    unionid: Optional[str] = None
    nickname: Optional[str] = None
    avatar_url: Optional[str] = None
    source: str = "miniapp"


class WechatSyncResponse(BaseModel):
    access_token: str
    token: str
    refresh_token: str
    user_id: str
    user: dict
    user_info: dict
    is_new_user: bool
    expires_in: int


def _get_internal_api_keys() -> list[str]:
    """获取内部 API Key 列表

    优先级:
    1. INTERNAL_API_KEYS 环境变量
    2. /app/generated/internal_api_keys.txt 文件 (volume 持久化)
    """
    env_keys = os.environ.get("INTERNAL_API_KEYS", "")
    if env_keys:
        return [k.strip() for k in env_keys.split(",") if k.strip()]

    # 从持久化文件读取
    try:
        with open("/app/generated/internal_api_keys.txt", "r") as f:
            content = f.read().strip()
            if content:
                return [k.strip() for k in content.split(",") if k.strip()]
    except Exception:
        logger.debug("Failed to read internal API keys from file", exc_info=True)

    return []


def _verify_internal_api_key(x_api_key: str | None = Header(None)) -> bool:
    """验证内部 API Key

    - 已配置时：必须匹配 INTERNAL_API_KEYS 中的 key
    - 未配置且非生产环境：放行（方便开发）
    - 未配置且生产环境：拒绝
    """
    env = os.environ.get("APP_ENV", os.environ.get("ENV", "development")).lower()
    key_list = _get_internal_api_keys()

    if not key_list:
        if env in ("production", "prod"):
            raise HTTPException(status_code=401, detail="内部接口未配置 API Key")
        return True

    if x_api_key and x_api_key.strip() in key_list:
        return True

    raise HTTPException(status_code=401, detail="无效的 API Key")


@router.post("/wechat-sync", response_model=WechatSyncResponse, include_in_schema=False)
async def wechat_sync(
    request: WechatSyncRequest,
    user_repository: UserRepository = Depends(get_user_repository),
    _: bool = Depends(_verify_internal_api_key),
):
    """
    微信同步登录/注册（系统级内部接口）

    由 BFF 层通过 API Key 调用，不直接面向终端用户。
    根据 openid 查找或创建用户，返回 SaaS token。
    """
    from packages.application.auth.wechat_sync_use_case import WechatSyncRequest as UseCaseRequest
    from packages.application.auth.wechat_sync_use_case import (
        WechatSyncUseCase,
    )

    use_case = WechatSyncUseCase(user_repository=user_repository)
    use_case_request = UseCaseRequest(
        openid=request.openid,
        unionid=request.unionid,
        nickname=request.nickname,
        avatar_url=request.avatar_url,
        source=request.source,
    )

    response, error = use_case.execute(use_case_request)
    if error:
        raise HTTPException(status_code=400, detail=error)

    return WechatSyncResponse(**response.to_dict())
