import os
import hashlib
import secrets
from datetime import datetime, timedelta, timezone
from typing import Optional, Tuple
from lic_webapp.database import get_db_connection

# Hash Settings
SALT_SIZE = 16
ITERATIONS = 100000
HASH_ALGO = 'sha256'
SESSION_LIFETIME_HOURS = 2

def hash_password(password: str) -> str:
    """
    Hash a password securely using PBKDF2-HMAC-SHA256.
    Returns: salt_hex:iterations:hash_hex
    """
    salt = os.urandom(SALT_SIZE)
    pwd_hash = hashlib.pbkdf2_hmac(
        HASH_ALGO,
        password.encode('utf-8'),
        salt,
        ITERATIONS
    )
    return f"{salt.hex()}:{ITERATIONS}:{pwd_hash.hex()}"

def verify_password(password: str, stored_hash: str) -> bool:
    """
    Verify a password against its stored hash.
    """
    try:
        parts = stored_hash.split(':')
        if len(parts) != 3:
            return False
        
        salt = bytes.fromhex(parts[0])
        iterations = int(parts[1])
        stored_pwd_hash = bytes.fromhex(parts[2])
        
        pwd_hash = hashlib.pbkdf2_hmac(
            HASH_ALGO,
            password.encode('utf-8'),
            salt,
            iterations
        )
        # Use constant time comparison
        return secrets.compare_digest(pwd_hash, stored_pwd_hash)
    except Exception:
        return False

def create_session(user_id: int) -> Tuple[str, str]:
    """
    Create a new user session in the database.
    Returns a tuple of (session_id, csrf_token).
    """
    session_id = secrets.token_hex(32)
    csrf_token = secrets.token_hex(32)
    expires_at = (datetime.now(timezone.utc) + timedelta(hours=SESSION_LIFETIME_HOURS)).isoformat()
    
    conn = get_db_connection()
    try:
        conn.execute(
            "INSERT INTO sessions (session_id, user_id, csrf_token, expires_at) VALUES (?, ?, ?, ?);",
            (session_id, user_id, csrf_token, expires_at)
        )
        conn.commit()
    finally:
        conn.close()
        
    return session_id, csrf_token

def validate_session(session_id: str) -> Optional[dict]:
    """
    Validate an active session. If valid, return a dict with user details:
    { "user_id": int, "username": str, "role": str, "csrf_token": str }
    """
    if not session_id:
        return None
        
    conn = get_db_connection()
    try:
        # Delete expired sessions on access to keep database clean
        now = datetime.now(timezone.utc).isoformat()
        conn.execute("DELETE FROM sessions WHERE expires_at < ?;", (now,))
        conn.commit()
        
        row = conn.execute(
            """
            SELECT s.user_id, s.csrf_token, u.username, u.role, u.rvp, u.email
            FROM sessions s
            JOIN users u ON s.user_id = u.id
            WHERE s.session_id = ? AND s.expires_at > ?;
            """,
            (session_id, now)
        )
        result = row.fetchone()
        if result:
            return {
                "user_id": result["user_id"],
                "username": result["username"],
                "role": result["role"],
                "rvp": result["rvp"],
                "email": result["email"],
                "csrf_token": result["csrf_token"]
            }
    finally:
        conn.close()
        
    return None

def delete_session(session_id: str) -> None:
    """
    Delete a session, effectively logging the user out.
    """
    if not session_id:
        return
    conn = get_db_connection()
    try:
        conn.execute("DELETE FROM sessions WHERE session_id = ?;", (session_id,))
        conn.commit()
    finally:
        conn.close()

def clean_expired_sessions() -> None:
    """
    Purge expired sessions from the database.
    """
    conn = get_db_connection()
    try:
        now = datetime.now(timezone.utc).isoformat()
        conn.execute("DELETE FROM sessions WHERE expires_at < ?;", (now,))
        conn.commit()
    finally:
        conn.close()
