import os  # 导入系统模块用于读取环境变量。 
import hashlib  # 导入哈希模块用于生成密码摘要。 
import hmac  # 导入 hmac 用于安全比对摘要。 
import pymysql  # 导入 PyMySQL 用于连接 MySQL 数据库。 
import config as _config  # 导入配置模块获取数据库参数。 
from typing import Tuple  # 导入 Tuple 用于返回值类型标注。 
DEFAULT_USERS = ["vip1", "vip2", "vip3", "vip4", "vip5", "vip6"]  # 默认允许登录的账号列表。 
DEFAULT_PASSWORD = _config.DEFAULT_LOGIN_PASSWORD  # 默认账号初始密码配置。 
HASH_ITERATIONS = 120000  # 密码哈希的迭代次数提高安全性。 
SALT_BYTES = 16  # 密码盐字节长度用于生成随机盐。 
def _get_env(key, default):  # 获取环境变量并提供默认值的函数。 
    value = os.getenv(key)  # 读取指定环境变量值。 
    return value if value else default  # 返回环境变量或默认值。 
def _get_db_config():  # 汇总数据库配置的函数。 
    host = _get_env("WAWACLOUD_DB_HOST", _config.DB_HOST)  # 获取数据库主机地址。 
    port_raw = _get_env("WAWACLOUD_DB_PORT", str(_config.DB_PORT))  # 获取数据库端口配置。 
    try:  # 捕获端口转换异常。 
        port = int(port_raw)  # 将端口转换为整数。 
    except ValueError:  # 端口不是数字时回退默认。 
        port = int(_config.DB_PORT)  # 使用配置文件中的默认端口。 
    user = _get_env("WAWACLOUD_DB_USER", _config.DB_USER)  # 获取数据库用户名。 
    password = _get_env("WAWACLOUD_DB_PASSWORD", _config.DB_PASSWORD)  # 获取数据库密码。 
    database = _get_env("WAWACLOUD_DB_NAME", _config.DB_NAME)  # 获取数据库名称。 
    charset = _get_env("WAWACLOUD_DB_CHARSET", _config.DB_CHARSET)  # 获取数据库字符集。 
    return host, port, user, password, database, charset  # 返回数据库配置元组。 
def _connect():  # 创建数据库连接的函数。 
    host, port, user, password, database, charset = _get_db_config()  # 读取数据库连接参数。 
    return pymysql.connect(  # 返回新的数据库连接对象。 
        host=host,  # 设置数据库主机。 
        port=port,  # 设置数据库端口。 
        user=user,  # 设置数据库用户名。 
        password=password,  # 设置数据库密码。 
        database=database,  # 设置数据库名称。 
        charset=charset,  # 设置数据库字符集。 
        cursorclass=pymysql.cursors.DictCursor,  # 使用字典游标便于字段访问。 
        autocommit=True,  # 自动提交避免手动 commit。 
    )  # 连接配置结束。 
def _hash_password(password: str, salt_hex: str = "") -> Tuple[str, str]:  # 生成密码哈希的函数。 
    salt = bytes.fromhex(salt_hex) if salt_hex else os.urandom(SALT_BYTES)  # 读取已有盐或生成随机盐。 
    digest = hashlib.pbkdf2_hmac(  # 使用 PBKDF2 生成密码摘要。 
        "sha256",  # 选择 SHA256 作为摘要算法。 
        password.encode("utf-8"),  # 将密码编码为字节数据。 
        salt,  # 使用盐提高安全性。 
        HASH_ITERATIONS,  # 设置迭代次数增强抗暴力破解能力。 
    )  # 摘要计算完成。 
    return digest.hex(), salt.hex()  # 返回哈希十六进制与盐十六进制。 
def init_db():  # 初始化数据库表结构的函数。 
    sql = (  # 定义用户表建表 SQL。 
        "CREATE TABLE IF NOT EXISTS users ("  # 创建用户表。 
        "id BIGINT AUTO_INCREMENT PRIMARY KEY,"  # 自增主键。 
        "username VARCHAR(64) NOT NULL UNIQUE,"  # 账号字段并保证唯一。 
        "password_hash VARCHAR(128) NOT NULL,"  # 密码哈希字段。 
        "salt VARCHAR(64) NOT NULL,"  # 盐字段。 
        "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,"  # 创建时间。 
        "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"  # 更新时间。 
        ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"  # 使用 InnoDB 和 utf8mb4。 
    )  # SQL 字符串定义结束。 
    conn = _connect()  # 获取数据库连接。 
    try:  # 捕获数据库执行异常。 
        with conn.cursor() as cursor:  # 创建游标用于执行 SQL。 
            cursor.execute(sql)  # 执行建表 SQL。 
    finally:  # 确保连接最终关闭。 
        conn.close()  # 关闭数据库连接。 
def ensure_default_users():  # 确保默认账号存在的函数。 
    conn = _connect()  # 获取数据库连接。 
    try:  # 捕获执行异常。 
        with conn.cursor() as cursor:  # 创建游标用于执行 SQL。 
            for username in DEFAULT_USERS:  # 遍历默认账号列表。 
                cursor.execute("SELECT id FROM users WHERE username=%s", (username,))  # 查询账号是否存在。 
                row = cursor.fetchone()  # 获取查询结果。 
                if row:  # 若账号已存在则跳过。 
                    continue  # 继续处理下一个账号。 
                password_hash, salt = _hash_password(DEFAULT_PASSWORD)  # 生成默认密码哈希与盐。 
                cursor.execute(  # 插入默认账号数据。 
                    "INSERT INTO users (username, password_hash, salt) VALUES (%s, %s, %s)",  # 插入 SQL。 
                    (username, password_hash, salt),  # 插入参数。 
                )  # 插入执行结束。 
    finally:  # 确保连接最终关闭。 
        conn.close()  # 关闭数据库连接。 
def authenticate_user(username: str, password: str) -> bool:  # 校验账号密码的函数。 
    conn = _connect()  # 获取数据库连接。 
    try:  # 捕获执行异常。 
        with conn.cursor() as cursor:  # 创建游标用于执行 SQL。 
            cursor.execute(  # 查询账号对应的密码哈希与盐。 
                "SELECT password_hash, salt FROM users WHERE username=%s",  # 查询 SQL。 
                (username,),  # 查询参数。 
            )  # SQL 执行结束。 
            row = cursor.fetchone()  # 获取查询结果。 
            if not row:  # 账号不存在直接返回失败。 
                return False  # 账号不存在。 
            stored_hash = row["password_hash"]  # 读取数据库保存的哈希值。 
            stored_salt = row["salt"]  # 读取数据库保存的盐值。 
            calc_hash, _ = _hash_password(password, stored_salt)  # 使用盐计算输入密码哈希。 
            return hmac.compare_digest(stored_hash, calc_hash)  # 安全比对哈希并返回结果。 
    finally:  # 确保连接最终关闭。 
        conn.close()  # 关闭数据库连接。 

def is_default_password(username: str) -> bool:  # 判断账号是否仍在使用默认密码。 
    if not username:  # 账号为空直接返回 False。 
        return False  # 空账号无需判断。 
    conn = _connect()  # 获取数据库连接。 
    try:  # 捕获执行异常。 
        with conn.cursor() as cursor:  # 创建游标用于执行 SQL。 
            cursor.execute(  # 查询账号对应的密码哈希与盐。 
                "SELECT password_hash, salt FROM users WHERE username=%s",  # 查询 SQL。 
                (username,),  # 查询参数。 
            )  # SQL 执行结束。 
            row = cursor.fetchone()  # 获取查询结果。 
            if not row:  # 账号不存在则返回 False。 
                return False  # 不存在默认密码判断。 
            stored_hash = row["password_hash"]  # 读取数据库保存的哈希值。 
            stored_salt = row["salt"]  # 读取数据库保存的盐值。 
            default_hash, _ = _hash_password(DEFAULT_PASSWORD, stored_salt)  # 计算默认密码哈希。 
            return hmac.compare_digest(stored_hash, default_hash)  # 比对是否为默认密码。 
    finally:  # 确保连接最终关闭。 
        conn.close()  # 关闭数据库连接。 
def update_password(username: str, old_password: str, new_password: str) -> Tuple[bool, str]:  # 修改密码的函数。 
    if not username:  # 校验账号不能为空。 
        return False, "账号不能为空"  # 返回错误信息。 
    if not new_password:  # 校验新密码不能为空。 
        return False, "新密码不能为空"  # 返回错误信息。 
    if len(new_password) < 6:  # 简单校验新密码长度。 
        return False, "新密码长度至少 6 位"  # 返回错误信息。 
    if not authenticate_user(username, old_password):  # 校验旧密码是否正确。 
        return False, "旧密码不正确"  # 返回错误信息。 
    password_hash, salt = _hash_password(new_password)  # 生成新密码哈希与盐。 
    conn = _connect()  # 获取数据库连接。 
    try:  # 捕获执行异常。 
        with conn.cursor() as cursor:  # 创建游标用于执行 SQL。 
            cursor.execute(  # 更新账号密码哈希与盐。 
                "UPDATE users SET password_hash=%s, salt=%s WHERE username=%s",  # 更新 SQL。 
                (password_hash, salt, username),  # 更新参数。 
            )  # SQL 执行结束。 
        return True, "密码修改成功"  # 返回成功信息。 
    finally:  # 确保连接最终关闭。 
        conn.close()  # 关闭数据库连接。 
