"""
微信同步登录/注册 Use Case

供 BFF 层调用的系统级接口：
- 根据 openid 查找用户，找到则登录返回 token
- 没找到则创建新用户并返回 token
- 支持 unionid 跨应用关联
"""

import secrets
from datetime import datetime, timedelta, timezone
from typing import Optional
from uuid import uuid4

import jwt as pyjwt

from packages.adapters.redis import get_session_store
from packages.application.auth.jwt_service import jwt_service
from packages.domain.entities import User


class WechatSyncRequest:
    """微信同步登录请求"""

    def __init__(
        self,
        openid: str,
        unionid: str = "",
        nickname: str = "",
        avatar_url: str = "",
        source: str = "miniapp",
    ):
        self.openid = openid.strip()
        self.unionid = unionid.strip() if unionid else ""
        self.nickname = nickname or "微信用户"
        self.avatar_url = avatar_url or ""
        self.source = source


class WechatSyncResponse:
    """微信同步登录响应"""

    def __init__(
        self,
        access_token: str,
        refresh_token: str,
        user_id: str,
        nickname: str,
        avatar_url: str,
        is_new_user: bool,
        expires_in: int,
    ):
        self.access_token = access_token
        self.refresh_token = refresh_token
        self.user_id = user_id
        self.nickname = nickname
        self.avatar_url = avatar_url
        self.is_new_user = is_new_user
        self.expires_in = expires_in

    def to_dict(self) -> dict:
        return {
            "access_token": self.access_token,
            "token": self.access_token,  # 兼容 BFF 层用 token 字段读取
            "refresh_token": self.refresh_token,
            "user_id": self.user_id,
            "user": {
                "id": self.user_id,
                "nickname": self.nickname,
                "avatar_url": self.avatar_url,
                "display_name": self.nickname,
            },
            "user_info": {
                "id": self.user_id,
                "nickname": self.nickname,
                "avatar_url": self.avatar_url,
                "display_name": self.nickname,
            },
            "is_new_user": self.is_new_user,
            "expires_in": self.expires_in,
        }


class WechatSyncUseCase:
    """微信同步登录/注册用例

    系统级接口，由 BFF 通过 API Key 调用。
    职责：根据 openid 查找或创建用户，返回 SaaS token。
    """

    def __init__(self, user_repository, session_store=None, jwt_secret_key: str | None = None):
        self.user_repository = user_repository
        self.session_store = session_store or get_session_store()
        self.jwt_secret_key = jwt_secret_key or jwt_service.config.SECRET_KEY

    def execute(self, request: WechatSyncRequest) -> tuple[Optional[WechatSyncResponse], Optional[str]]:
        """
        执行微信同步登录/注册

        Returns:
            (响应对象, 错误信息) - 成功则错误信息为 None
        """
        try:
            if not request.openid:
                return None, "openid is required"

            is_new_user = False

            # 1. 按 openid 查找用户
            user = self.user_repository.find_by_wechat_openid(request.openid)

            # 2. 如果 openid 没找到，尝试 unionid
            if not user and request.unionid:
                user = self.user_repository.find_by_wechat_unionid(request.unionid)
                if user:
                    # 找到用户但 openid 为空，绑定一下当前 openid
                    user.wechat_openid = request.openid
                    self.user_repository.save(user)

            # 3. 都没找到则创建新用户
            if not user:
                user = self._create_wechat_user(request)
                is_new_user = True

            # 4. 创建 session 并生成 token
            session_id = secrets.token_urlsafe(16)
            refresh_token = secrets.token_urlsafe(32)

            now = datetime.now(timezone.utc)
            access_token_payload = {
                "sub": user.id,
                "sid": session_id,
                "type": "user_auth",
                "iat": now,
                "exp": now + timedelta(minutes=jwt_service.config.ACCESS_TOKEN_EXPIRE_MINUTES),
            }
            access_token = pyjwt.encode(
                access_token_payload,
                self.jwt_secret_key,
                algorithm=jwt_service.config.ALGORITHM,
            )

            # 保存 session
            self.session_store.save_session(
                session_id=session_id,
                user_id=user.id,
                refresh_token=refresh_token,
                device_info=f"wechat_{request.source}",
                ip_address="bff_gateway",
                expires_in_seconds=30 * 24 * 3600,  # 30 天
            )

            # 更新最后登录信息
            user.last_login_at = now
            user.last_login_ip = "bff_gateway"
            self.user_repository.save(user)

            response = WechatSyncResponse(
                access_token=access_token,
                refresh_token=refresh_token,
                user_id=user.id,
                nickname=user.display_name,
                avatar_url="",  # SaaS 用户模型暂存头像，后续可扩展
                is_new_user=is_new_user,
                expires_in=jwt_service.config.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
            )

            return response, None

        except Exception as e:
            return None, f"Internal error: {str(e)}"

    def _create_wechat_user(self, request: WechatSyncRequest) -> User:
        """创建微信用户"""
        user_id = uuid4().hex

        # 生成唯一名和邮箱（微信用户无真实邮箱，用 openid 生成占位）
        safe_openid = request.openid.replace("-", "_")[:20]
        username = f"wx_{safe_openid}"
        email = f"{safe_openid}@wechat.local"

        # 确保 username 唯一
        suffix = 0
        while self.user_repository.find_by_username(username):
            suffix += 1
            username = f"wx_{safe_openid}_{suffix}"

        # 随机密码（微信用户不用密码登录）
        random_password = secrets.token_urlsafe(32)
        from packages.application.auth.password_hasher import password_hasher

        password_hash = password_hasher.hash_password(random_password)

        user = User(
            id=user_id,
            email=email,
            username=username,
            display_name=request.nickname or "微信用户",
            password_hash=password_hash,
            email_verified=True,  # 微信登录视为已验证
            wechat_openid=request.openid,
            wechat_unionid=request.unionid or None,
        )

        self.user_repository.save(user)
        return user
