"""
密碼服務模組

提供：
- 密碼複雜度驗證
- 密碼雜湊與驗證
- 密碼過期檢查
"""

import re
from datetime import datetime, timedelta
from werkzeug.security import generate_password_hash, check_password_hash
from config import (
    PASSWORD_MIN_LENGTH,
    PASSWORD_REQUIRE_UPPERCASE,
    PASSWORD_REQUIRE_LOWERCASE,
    PASSWORD_REQUIRE_DIGIT,
    PASSWORD_REQUIRE_SPECIAL,
    PASSWORD_SPECIAL_CHARS,
    PASSWORD_EXPIRY_DAYS,
)


def validate_password_complexity(password):
    """
    驗證密碼複雜度

    根據 config.py 中的設定檢查密碼是否符合要求：
    - 最小長度
    - 大寫字母
    - 小寫字母
    - 數字
    - 特殊符號

    Args:
        password: 要驗證的密碼

    Returns:
        tuple: (is_valid, error_message)
            - is_valid: 是否符合要求
            - error_message: 錯誤訊息（符合時為 None）
    """
    if not password:
        return False, "密碼不能為空"

    errors = []

    # 檢查長度
    if len(password) < PASSWORD_MIN_LENGTH:
        errors.append(f"密碼長度至少需要 {PASSWORD_MIN_LENGTH} 個字元")

    # 檢查大寫字母
    if PASSWORD_REQUIRE_UPPERCASE and not re.search(r'[A-Z]', password):
        errors.append("密碼必須包含至少一個大寫字母")

    # 檢查小寫字母
    if PASSWORD_REQUIRE_LOWERCASE and not re.search(r'[a-z]', password):
        errors.append("密碼必須包含至少一個小寫字母")

    # 檢查數字
    if PASSWORD_REQUIRE_DIGIT and not re.search(r'\d', password):
        errors.append("密碼必須包含至少一個數字")

    # 檢查特殊符號
    if PASSWORD_REQUIRE_SPECIAL:
        # 轉義特殊字元用於正則表達式
        escaped_chars = re.escape(PASSWORD_SPECIAL_CHARS)
        if not re.search(f'[{escaped_chars}]', password):
            errors.append(f"密碼必須包含至少一個特殊符號 ({PASSWORD_SPECIAL_CHARS})")

    if errors:
        return False, "、".join(errors)

    return True, None


def get_password_requirements():
    """
    取得密碼複雜度要求說明

    Returns:
        list: 密碼要求的說明列表
    """
    requirements = [f"至少 {PASSWORD_MIN_LENGTH} 個字元"]

    if PASSWORD_REQUIRE_UPPERCASE:
        requirements.append("至少一個大寫字母 (A-Z)")
    if PASSWORD_REQUIRE_LOWERCASE:
        requirements.append("至少一個小寫字母 (a-z)")
    if PASSWORD_REQUIRE_DIGIT:
        requirements.append("至少一個數字 (0-9)")
    if PASSWORD_REQUIRE_SPECIAL:
        requirements.append(f"至少一個特殊符號 ({PASSWORD_SPECIAL_CHARS})")

    return requirements


def hash_password(password):
    """
    將密碼進行雜湊處理

    Args:
        password: 明文密碼

    Returns:
        str: 雜湊後的密碼
    """
    return generate_password_hash(password, method='pbkdf2:sha256')


def verify_password(password_hash, password):
    """
    驗證密碼是否正確

    Args:
        password_hash: 資料庫中儲存的雜湊密碼
        password: 用戶輸入的明文密碼

    Returns:
        bool: 密碼是否正確
    """
    return check_password_hash(password_hash, password)


def is_password_expired(password_changed_at):
    """
    檢查密碼是否已過期

    Args:
        password_changed_at: 密碼上次變更時間 (datetime 或 None)

    Returns:
        tuple: (is_expired, days_until_expiry)
            - is_expired: 是否已過期
            - days_until_expiry: 距離過期還有幾天（已過期時為負數）
    """
    # 如果沒有設定過期天數（0 或 None），則永不過期
    if not PASSWORD_EXPIRY_DAYS:
        return False, None

    # 如果沒有密碼變更時間，視為需要立即變更
    if not password_changed_at:
        return True, 0

    expiry_date = password_changed_at + timedelta(days=PASSWORD_EXPIRY_DAYS)
    now = datetime.now()
    days_until_expiry = (expiry_date - now).days

    if days_until_expiry < 0:
        return True, days_until_expiry
    else:
        return False, days_until_expiry


def get_password_expiry_warning(password_changed_at, warning_days=14):
    """
    取得密碼過期警告訊息

    Args:
        password_changed_at: 密碼上次變更時間
        warning_days: 幾天前開始警告

    Returns:
        str or None: 警告訊息，若不需警告則為 None
    """
    is_expired, days_until = is_password_expired(password_changed_at)

    if is_expired:
        if days_until is None:
            return None  # 永不過期
        return f"您的密碼已過期，請立即變更密碼。"

    if days_until is not None and days_until <= warning_days:
        return f"您的密碼將在 {days_until} 天後過期，請及早變更密碼。"

    return None


print("✅ Password service 已載入")
