"""
用户认证 API（单机版 v1.3.0）
- 注册（首次启动）
- 登录（后续启动）
- 重置（忘记密码）
- 状态检查
- 个人中心
"""
import hashlib
import secrets
import uuid
import logging
from typing import Optional
from fastapi import APIRouter, HTTPException, Depends, Request
from pydantic import BaseModel, Field
from ...core.database import get_db
from ...core.permission import require_login, get_current_user
from ...core.auth import generate_token

router = APIRouter()
logger = logging.getLogger(__name__)

# 内存中的登录 token（单用户，进程级）
_login_token: str | None = None


class RegisterRequest(BaseModel):
    username: str = Field(..., min_length=4, max_length=20, pattern=r'^[a-zA-Z0-9_]+$')
    password: str = Field(..., min_length=6, max_length=16)
    confirm_password: str = Field(..., min_length=6, max_length=16)


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


MAX_LOGIN_ATTEMPTS = 5
LOCKOUT_MINUTES = 15

class ChangeUsernameRequest(BaseModel):
    password: str = Field(..., min_length=6, max_length=16, description="当前密码")
    new_username: str = Field(..., min_length=4, max_length=20, pattern=r'^[a-zA-Z0-9_]+$', description="新用户名")


class ChangePasswordRequest(BaseModel):
    old_password: str = Field(..., min_length=6, max_length=16, description="当前密码")
    new_password: str = Field(..., min_length=6, max_length=16, description="新密码")
    confirm_password: str = Field(..., min_length=6, max_length=16, description="确认新密码")



def _hash_password(password: str, salt: str = "zfzs_standalone") -> str:
    return hashlib.sha256(f"{salt}_{password}".encode()).hexdigest()


def _verify_password(password: str, stored_hash: str) -> bool:
    """验证密码，兼容 bcrypt 和 SHA-256 两种格式"""
    # 先尝试 bcrypt（旧系统迁移的用户）
    if stored_hash.startswith("$2b$") or stored_hash.startswith("$2a$"):
        try:
            import bcrypt
            return bcrypt.checkpw(password.encode("utf-8"), stored_hash.encode("utf-8"))
        except ImportError:
            pass
    # 尝试 SHA-256（新注册用户）
    return stored_hash == _hash_password(password)


def get_login_token() -> str | None:
    """获取当前登录 token（供中间件检查）"""
    return _login_token


def clear_login_token():
    """清除登录 token（反爬中间件调用，强制用户重新登录）"""
    global _login_token
    _login_token = None


@router.get("/status")
async def user_status(request: Request):
    """检查是否已注册 + 是否已登录（基于当前请求的 token）"""
    async for conn, cur in get_db():
        await cur.execute("SELECT COUNT(*) as cnt FROM users WHERE is_active = 1")
        row = await cur.fetchone()
        cnt = row["cnt"] if isinstance(row, dict) else row[0]
        is_registered = cnt > 0
        # 基于当前请求的 Authorization 头判断登录状态，而非全局变量
        user = get_current_user(request)
        is_logged_in = user is not None
        return {"is_registered": is_registered, "is_logged_in": is_logged_in}


@router.post("/register")
async def user_register(body: RegisterRequest):
    """注册新用户（支持创建多个用户）"""
    global _login_token
    try:
        if body.password != body.confirm_password:
            raise HTTPException(400, "两次密码不一致")

        async for conn, cur in get_db():
            # 检查用户名重复（仅检查活跃用户）
            await cur.execute("SELECT id FROM users WHERE username = ? AND is_active = 1", (body.username,))
            if await cur.fetchone():
                raise HTTPException(400, "用户名已存在")

            # 创建用户
            user_id = str(uuid.uuid4())
            pwd_hash = _hash_password(body.password)
            await cur.execute(
                "INSERT INTO users (id, username, password_hash) VALUES (?, ?, ?)",
                (user_id, body.username, pwd_hash),
            )
            await conn.commit()

            # 自动登录（生成 JWT token，兼容 require_login 鉴权）
            from ...core.auth import generate_token as _gen_token
            jwt_token = _gen_token(user_id, body.username, "user")
            _login_token = jwt_token
            return {
                "success": True,
                "message": "注册成功",
                "token": jwt_token,
                "user": {
                    "id": user_id,
                    "username": body.username,
                    "real_name": body.username,
                    "role": "user",
                    "level": "",
                    "group_id": None,
                },
                "menus": [],
                "warning": None,
            }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"注册失败: {e}", exc_info=True)
        raise HTTPException(500, f"注册失败: {e}")


@router.post("/login")
async def user_login(body: LoginRequest):
    """用户登录（含登录失败限制）"""
    global _login_token
    try:
        async for conn, cur in get_db():
            await cur.execute(
                "SELECT id, username, password_hash, real_name, role, level, group_id, "
                "login_attempts, locked_until FROM users WHERE username = ? AND is_active = 1",
                (body.username,),
            )
            row = await cur.fetchone()
            if not row:
                raise HTTPException(401, "用户名或密码错误")

            row_dict = dict(row)

            # 检查账户锁定
            locked_until = row_dict.get("locked_until")
            if locked_until:
                from datetime import datetime as _dt
                try:
                    lock_dt = _dt.fromisoformat(locked_until)
                    if _dt.now() < lock_dt:
                        remaining = int((lock_dt - _dt.now()).total_seconds() / 60) + 1
                        raise HTTPException(423, f"账户已锁定，请 {remaining} 分钟后再试")
                except HTTPException:
                    raise
                except Exception:
                    pass  # 时间解析失败，忽略锁定

            if not _verify_password(body.password, row_dict["password_hash"]):
                # 登录失败，增加计数
                attempts = (row_dict.get("login_attempts") or 0) + 1
                if attempts >= MAX_LOGIN_ATTEMPTS:
                    from datetime import datetime as _dt, timedelta as _td
                    lock_time = (_dt.now() + _td(minutes=LOCKOUT_MINUTES)).isoformat()
                    await cur.execute(
                        "UPDATE users SET login_attempts = ?, locked_until = ? WHERE id = ?",
                        (attempts, lock_time, row_dict["id"]),
                    )
                    await conn.commit()
                    raise HTTPException(423, f"连续 {MAX_LOGIN_ATTEMPTS} 次登录失败，账户已锁定 {LOCKOUT_MINUTES} 分钟")
                else:
                    await cur.execute(
                        "UPDATE users SET login_attempts = ? WHERE id = ?",
                        (attempts, row_dict["id"]),
                    )
                    await conn.commit()
                    raise HTTPException(401, f"用户名或密码错误（{MAX_LOGIN_ATTEMPTS - attempts} 次机会剩余）")

            # 登录成功，重置失败计数
            await cur.execute(
                "UPDATE users SET login_attempts = 0, locked_until = NULL, last_login_at = datetime('now') WHERE id = ?",
                (row_dict["id"],),
            )
            await conn.commit()

            # 生成 JWT token（兼容 require_login 鉴权）
            jwt_token = generate_token(
                row_dict["id"], row_dict["username"],
                row_dict.get("role") or "user"
            )

            # 查询用户菜单权限（group_menus 表可能不存在）
            menus = []
            if row_dict.get("group_id"):
                try:
                    await cur.execute(
                        "SELECT menu_code, menu_name, parent_code, icon, route_path, sort_order "
                        "FROM group_menus WHERE group_id = ? ORDER BY sort_order",
                        (row_dict["group_id"],),
                    )
                    menus = [dict(m) for m in await cur.fetchall()]
                except Exception:
                    pass  # group_menus 表不存在时忽略

            return {
                "success": True,
                "token": jwt_token,
                "user": {
                    "id": row_dict["id"],
                    "username": row_dict["username"],
                    "real_name": row_dict.get("real_name") or row_dict["username"],
                    "role": row_dict.get("role") or "user",
                    "level": row_dict.get("level") or "",
                    "group_id": row_dict.get("group_id"),
                },
                "menus": menus,
                "warning": None,
            }
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logging.getLogger(__name__).error(f"登录异常: {e}", exc_info=True)
        raise HTTPException(500, f"登录失败: {e}")


class ResetRequest(BaseModel):
    password: Optional[str] = Field(default=None, description="当前用户密码（注册页重置时可不传）")
    confirm: str = Field(..., min_length=1, description="输入 CONFIRM 确认重置操作")

    def model_post_init(self, __context) -> None:
        if self.password is not None and (len(self.password) < 6 or len(self.password) > 16):
            raise ValueError("密码长度必须为 6-16 位")


@router.post("/reset")
async def user_reset(body: ResetRequest):
    """重置账户（忘记密码）— 需二次确认；注册页调用时无需密码验证"""
    global _login_token

    if body.confirm != "CONFIRM":
        raise HTTPException(400, "请输入 CONFIRM 确认重置操作")

    async for conn, cur in get_db():
        # 如果提供了密码，验证当前密码
        if body.password:
            await cur.execute("SELECT id, password_hash FROM users WHERE is_active = 1")
            row = await cur.fetchone()
            if not row:
                raise HTTPException(400, "无活跃用户")

            row_dict = dict(row)
            if not _verify_password(body.password, row_dict["password_hash"]):
                raise HTTPException(401, "密码错误，无法执行重置")

        # 硬删除用户（避免 username UNIQUE 约束导致重新注册失败）
        await cur.execute("DELETE FROM users")
        # 清除用户记忆
        try:
            await cur.execute("DELETE FROM user_memories")
        except Exception:
            pass
        # 清除案件数据
        try:
            await cur.execute("DELETE FROM case_documents")
        except Exception:
            pass
        try:
            await cur.execute("DELETE FROM case_logs")
        except Exception:
            pass
        try:
            await cur.execute("DELETE FROM cases")
        except Exception:
            pass
        # 清除电子取证清单
        try:
            await cur.execute("DELETE FROM evidence_checklist_items")
        except Exception:
            pass
        try:
            await cur.execute("DELETE FROM evidence_checklists")
        except Exception:
            pass
        # 注意：不删除 license_info（激活状态绑定机器码）
        await conn.commit()

    _login_token = None
    return {"success": True, "message": "账户已重置，请重新注册"}


# ── 以下为原有接口（保留兼容） ──


@router.put("/username")
async def change_username(body: ChangeUsernameRequest, user: dict = Depends(require_login), db=Depends(get_db)):
    """Change login username"""
    conn, cur = db
    user_id = user.get("userId", "default-user")

    await cur.execute(
        "SELECT id, username, password_hash, role FROM users WHERE id = ? AND is_active = 1",
        (user_id,),
    )
    row = await cur.fetchone()
    if not row:
        raise HTTPException(404, "用户不存在")

    row_dict = dict(row)

    if not _verify_password(body.password, row_dict["password_hash"]):
        raise HTTPException(400, "当前密码错误")

    await cur.execute(
        "SELECT id FROM users WHERE username = ? AND id != ? AND is_active = 1",
        (body.new_username, user_id),
    )
    if await cur.fetchone():
        raise HTTPException(400, "用户名已存在")

    await cur.execute(
        "UPDATE users SET username = ?, updated_at = datetime('now') WHERE id = ?",
        (body.new_username, user_id),
    )
    await conn.commit()

    new_token = generate_token(
        user_id, body.new_username,
        row_dict.get("role") or "user"
    )

    return {
        "success": True,
        "message": "用户名修改成功",
        "token": new_token,
        "username": body.new_username,
    }


@router.put("/password")
async def change_password(body: ChangePasswordRequest, user: dict = Depends(require_login), db=Depends(get_db)):
    """Change login password"""
    if body.new_password != body.confirm_password:
        raise HTTPException(400, "两次新密码不一致")

    conn, cur = db
    user_id = user.get("userId", "default-user")

    await cur.execute(
        "SELECT id, username, password_hash, role FROM users WHERE id = ? AND is_active = 1",
        (user_id,),
    )
    row = await cur.fetchone()
    if not row:
        raise HTTPException(404, "用户不存在")

    row_dict = dict(row)

    if not _verify_password(body.old_password, row_dict["password_hash"]):
        raise HTTPException(400, "当前密码错误")

    new_hash = _hash_password(body.new_password)
    await cur.execute(
        "UPDATE users SET password_hash = ?, updated_at = datetime('now') WHERE id = ?",
        (new_hash, user_id),
    )
    await conn.commit()

    return {
        "success": True,
        "message": "密码修改成功",
    }


@router.post("/logout")
async def user_logout(request: Request, user: dict = Depends(require_login)):
    global _login_token
    _login_token = None
    return {"message": "退出成功"}


@router.get("/profile")
async def get_user_profile(user: dict = Depends(require_login), db=Depends(get_db)):
    """获取用户个人中心信息（单机版：7 个可编辑字段）"""
    conn, cur = db
    user_id = user.get("userId", "default-user")

    profile = {
        "id": user["userId"],
        "username": user["username"],
        "name": "",
        "badge_no": "",
        "phone": "",
        "area": "",
        "org_name": "",
        "org_phone": "",
        "org_address": "",
    }

    try:
        await cur.execute(
            "SELECT content FROM user_memories WHERE user_id = ? AND memory_type = 'user_profile'",
            (user_id,),
        )
        row = await cur.fetchone()
        if row and dict(row).get("content"):
            import json
            saved = json.loads(row["content"])
            basic_info = saved.get("basic_info", {})
            if basic_info.get("name"):
                profile["name"] = basic_info["name"]
                profile["badge_no"] = basic_info.get("badge_no", "")
                profile["phone"] = basic_info.get("phone", "")
                profile["area"] = basic_info.get("area", "")
                profile["org_name"] = basic_info.get("org_name", "")
                profile["org_phone"] = basic_info.get("org_phone", "")
                profile["org_address"] = basic_info.get("org_address", "")
    except Exception:
        pass

    return profile


class ProfileUpdate(BaseModel):
    name: str = ""
    badge_no: str = ""
    phone: str = ""
    area: str = ""
    org_name: str = ""
    org_phone: str = ""
    org_address: str = ""


@router.put("/profile")
async def update_user_profile(body: ProfileUpdate, user: dict = Depends(require_login), db=Depends(get_db)):
    """更新用户个人中心信息"""
    import json
    conn, cur = db
    user_id = user.get("userId", "default-user")

    basic_info = {
        "name": body.name,
        "badge_no": body.badge_no,
        "phone": body.phone,
        "area": body.area,
        "org_name": body.org_name,
        "org_phone": body.org_phone,
        "org_address": body.org_address,
    }

    existing = {"basic_info": basic_info, "ai_preferences": ""}
    try:
        await cur.execute(
            "SELECT content FROM user_memories WHERE user_id = ? AND memory_type = 'user_profile'",
            (user_id,),
        )
        row = await cur.fetchone()
        if row and dict(row).get("content"):
            saved = json.loads(row["content"])
            saved["basic_info"] = basic_info
            existing = saved
    except Exception:
        pass

    content_json = json.dumps(existing, ensure_ascii=False)

    try:
        await cur.execute(
            "SELECT id FROM user_memories WHERE user_id = ? AND memory_type = 'user_profile'",
            (user_id,),
        )
        exists = await cur.fetchone()

        if exists:
            await cur.execute(
                "UPDATE user_memories SET content = ?, updated_at = datetime('now') WHERE user_id = ? AND memory_type = 'user_profile'",
                (content_json, user_id),
            )
        else:
            await cur.execute(
                "INSERT INTO user_memories (user_id, memory_type, content, created_at, updated_at) VALUES (?, 'user_profile', ?, datetime('now'), datetime('now'))",
                (user_id, content_json),
            )
        await conn.commit()
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"保存失败: {e}")

    return {"success": True, **basic_info}


@router.get("/pro-mode/status")
async def get_pro_mode_status(user: dict = Depends(require_login)):
    return {"pro_mode_available": False}


@router.get("/users/group-members")
async def get_group_members(user: dict = Depends(require_login)):
    return {"list": []}
