import hashlib
import hmac
import json
import time
import base64
import uuid
import os
import secrets
from typing import Optional, Dict, Any
from app.models.user import get_db_connection
from app.config import settings

COOKIE_NAME = "sf_token"
X_AUTH_HEADER = "X-Auth-Token"

def _load_or_create_secret_key() -> str:
    """Persistent random SECRET_KEY.

    Priority: env SECRET_KEY > {STORAGE_DIR}/.secret_key (auto-generated on
    first run). Never falls back to a hardcoded value: a known secret lets
    anyone forge admin tokens.
    """
    env_key = os.getenv("SECRET_KEY", "").strip()
    if env_key:
        return env_key
    key_file = os.path.join(settings.STORAGE_DIR, ".secret_key")
    try:
        os.makedirs(settings.STORAGE_DIR, exist_ok=True)
        if os.path.exists(key_file):
            with open(key_file, "r") as f:
                key = f.read().strip()
            if len(key) >= 32:
                return key
        key = secrets.token_hex(32)
        with open(key_file, "w") as f:
            f.write(key)
        return key
    except Exception:
        # Last resort: ephemeral random key (all tokens invalid on restart).
        return secrets.token_hex(32)

SECRET_KEY = _load_or_create_secret_key()

def hash_password(password: str, salt: Optional[str] = None) -> str:
    """
    Hash password using PBKDF2 HMAC SHA-256 with a per-user random salt.
    Guarantees raw passwords are NEVER stored or exposed in plaintext.
    Returns format: 'salt_hex:hashed_key_hex' (colon-delimited, stores both values).
    If salt is provided, uses that salt (for verification).
    If salt is None, generates a new 32-byte random salt.
    """
    if salt is None:
        salt = secrets.token_hex(32)
    key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt.encode('utf-8'), 600000)
    return f"{salt}:{key.hex()}"

def verify_password(plain_password: str, stored_value: str) -> bool:
    """Verify plain password against stored 'salt:hash' using constant-time comparison.
    Also supports legacy-format hash (hex only, without salt) for backward compatibility."""
    if ':' in stored_value:
        parts = stored_value.split(':', 1)
        salt = parts[0]
        expected_hash = parts[1]
        computed = hash_password(plain_password, salt)
        computed_hash = computed.split(':', 1)[1]
        return hmac.compare_digest(computed_hash, expected_hash)
    else:
        expected_hash = stored_value
        computed_hash = hashlib.pbkdf2_hmac('sha256', plain_password.encode('utf-8'), b"sonicforge_crypto_salt_2026_secure_", 600000).hex()
        return hmac.compare_digest(computed_hash, expected_hash)

def create_token(user_id: str, username: str, role: str, must_change_password: bool) -> str:
    payload = {
        "user_id": user_id,
        "username": username,
        "role": role,
        "must_change_password": bool(must_change_password),
        "exp": time.time() + (3600 * 24 * 7) # 7 days
    }
    payload_str = base64.b64encode(json.dumps(payload).encode("utf-8")).decode("utf-8")
    sig = hmac.new(SECRET_KEY.encode("utf-8"), payload_str.encode("utf-8"), hashlib.sha256).hexdigest()
    return f"{payload_str}.{sig}"

def decode_token(token: str) -> Optional[Dict[str, Any]]:
    try:
        parts = token.split(".")
        if len(parts) != 2:
            return None
        payload_str, sig = parts[0], parts[1]
        expected_sig = hmac.new(SECRET_KEY.encode("utf-8"), payload_str.encode("utf-8"), hashlib.sha256).hexdigest()
        if not hmac.compare_digest(sig, expected_sig):
            return None
        
        payload_bytes = base64.b64decode(payload_str.encode("utf-8"))
        payload = json.loads(payload_bytes.decode("utf-8"))
        if time.time() > payload.get("exp", 0):
            return None
        return payload
    except Exception:
        return None

def seed_admin():
    """Seed default admin account on initial launch only — never resets existing password."""
    conn = get_db_connection()
    cursor = conn.cursor()
    
    default_pwd = (os.getenv("DEFAULT_ADMIN_PASSWORD") or "admin123").strip()
    hashed_pwd = hash_password(default_pwd)
    now = time.time()
    
    cursor.execute("SELECT id FROM users WHERE username = ?", ("admin",))
    if not cursor.fetchone():
        admin_id = str(uuid.uuid4())
        cursor.execute("""
        INSERT INTO users (id, username, email, hashed_password, role, must_change_password, created_at, is_active)
        VALUES (?, ?, ?, ?, ?, 1, ?, 1)
        """, (admin_id, "admin", "admin@sonicforge.studio", hashed_pwd, "admin", now))
        
        cursor.execute("""
        INSERT INTO user_quotas (user_id, storage_limit_mb, max_tracks)
        VALUES (?, 10240, 64)
        """, (admin_id,))
        conn.commit()
            
    conn.close()

# Auto seed on module load
seed_admin()
