import os
import hashlib
import hmac
import secrets
import json
from datetime import datetime, timezone, timedelta
from functools import wraps
from flask import request, jsonify
from db import get_db

AUTH_SECRET = os.environ.get('WCPY_AUTH_SECRET', 'wcpy_sec_jwt_auth_secret_key_2026_ejal')

def hash_password(password: str) -> str:
    """Hashes a password using PBKDF2-HMAC-SHA256 with a unique salt."""
    salt = secrets.token_hex(16)
    iterations = 100_000
    derived = hashlib.pbkdf2_hmac(
        'sha256',
        password.encode('utf-8'),
        salt.encode('utf-8'),
        iterations
    )
    return f"pbkdf2:sha256:{iterations}${salt}${derived.hex()}"

def verify_password(password: str, stored_hash: str) -> bool:
    """Verifies a password against a stored PBKDF2 hash."""
    try:
        parts = stored_hash.split('$')
        if len(parts) != 3:
            return False
        algorithm_meta, salt, expected_hash = parts
        _, _, iterations_str = algorithm_meta.split(':')
        iterations = int(iterations_str)

        derived = hashlib.pbkdf2_hmac(
            'sha256',
            password.encode('utf-8'),
            salt.encode('utf-8'),
            iterations
        )
        return hmac.compare_digest(derived.hex(), expected_hash)
    except Exception:
        return False

def generate_api_key() -> str:
    """Generates a secure API key."""
    return f"wcpy_live_{secrets.token_hex(20)}"

def create_session(user_id: int, ip_address: str = None, user_agent: str = None, duration_days: int = 30) -> str:
    """Creates a new session token in the database and returns it."""
    token = secrets.token_urlsafe(32)
    now = datetime.now(timezone.utc)
    expires = now + timedelta(days=duration_days)

    conn = get_db()
    cursor = conn.cursor()
    cursor.execute("""
    INSERT INTO user_sessions (token, user_id, created_at, expires_at, ip_address, user_agent)
    VALUES (?, ?, ?, ?, ?, ?)
    """, (token, user_id, now.isoformat(), expires.isoformat(), ip_address or '', user_agent or ''))
    conn.commit()
    conn.close()
    return token

def destroy_session(token: str):
    """Destroys a session token."""
    if not token:
        return
    conn = get_db()
    cursor = conn.cursor()
    cursor.execute("DELETE FROM user_sessions WHERE token = ?", (token,))
    conn.commit()
    conn.close()

def get_active_sessions(user_id: int):
    """Retrieves all non-expired sessions for a user."""
    conn = get_db()
    cursor = conn.cursor()
    now_iso = datetime.now(timezone.utc).isoformat()
    cursor.execute("""
    SELECT token, user_id, created_at, expires_at, ip_address, user_agent
    FROM user_sessions
    WHERE user_id = ? AND expires_at > ?
    ORDER BY created_at DESC
    """, (user_id, now_iso))
    rows = cursor.fetchall()
    conn.close()
    return [dict(r) for r in rows]

def terminate_user_sessions(user_id: int, except_token: str = None):
    """Terminates active sessions for a user, optionally exempting except_token."""
    conn = get_db()
    cursor = conn.cursor()
    if except_token:
        cursor.execute("DELETE FROM user_sessions WHERE user_id = ? AND token != ?", (user_id, except_token))
    else:
        cursor.execute("DELETE FROM user_sessions WHERE user_id = ?", (user_id,))
    conn.commit()
    conn.close()

def get_user_by_id(user_id: int):
    """Retrieves full user record by ID."""
    conn = get_db()
    cursor = conn.cursor()
    cursor.execute("""
    SELECT u.*, p.name as package_name, p.price_monthly, p.base_monthly_credits, p.over_quota_policy, p.features as package_features
    FROM users u
    LEFT JOIN packages p ON u.package_id = p.id
    WHERE u.id = ?
    """, (user_id,))
    row = cursor.fetchone()
    conn.close()
    return dict(row) if row else None

def get_user_by_email(email: str):
    """Retrieves full user record by Email."""
    conn = get_db()
    cursor = conn.cursor()
    cursor.execute("""
    SELECT u.*, p.name as package_name, p.price_monthly, p.base_monthly_credits, p.over_quota_policy, p.features as package_features
    FROM users u
    LEFT JOIN packages p ON u.package_id = p.id
    WHERE LOWER(u.email) = LOWER(?)
    """, (email.strip(),))
    row = cursor.fetchone()
    conn.close()
    return dict(row) if row else None

def get_authenticated_user(req):
    """Extracts authenticated user from Request (Bearer Token, Cookie, or API Key)."""
    token = None
    auth_header = req.headers.get('Authorization', '')
    if auth_header.startswith('Bearer '):
        token = auth_header[7:].strip()
    
    if not token:
        token = req.cookies.get('wcpy_token') or req.args.get('token') or req.args.get('api_key')

    if not token:
        return None

    conn = get_db()
    cursor = conn.cursor()

    # Check active session token
    cursor.execute("""
    SELECT s.user_id, s.expires_at, u.*, p.name as package_name, p.price_monthly, p.base_monthly_credits, p.over_quota_policy, p.features as package_features
    FROM user_sessions s
    JOIN users u ON s.user_id = u.id
    LEFT JOIN packages p ON u.package_id = p.id
    WHERE s.token = ?
    """, (token,))
    row = cursor.fetchone()

    if row:
        expires_at_str = row['expires_at']
        try:
            expires_at = datetime.fromisoformat(expires_at_str)
            if expires_at.tzinfo is None:
                expires_at = expires_at.replace(tzinfo=timezone.utc)
            if datetime.now(timezone.utc) <= expires_at:
                conn.close()
                return dict(row)
            else:
                # Expired session, remove it
                cursor.execute("DELETE FROM user_sessions WHERE token = ?", (token,))
                conn.commit()
        except Exception:
            pass

    # Check API Key fallback
    cursor.execute("""
    SELECT u.*, p.name as package_name, p.price_monthly, p.base_monthly_credits, p.over_quota_policy, p.features as package_features
    FROM users u
    LEFT JOIN packages p ON u.package_id = p.id
    WHERE u.api_key = ?
    """, (token,))
    row = cursor.fetchone()
    conn.close()
    return dict(row) if row else None

def seed_default_users_if_needed():
    """Seeds default administrator and demo user accounts if not present."""
    conn = get_db()
    cursor = conn.cursor()

    cursor.execute("SELECT COUNT(*) as count FROM users")
    count = cursor.fetchone()['count']

    now_iso = datetime.now(timezone.utc).isoformat()

    if count == 0:
        # Seed Admin User
        admin_pass_hash = hash_password("Admin@WCPY2026!")
        admin_api_key = generate_api_key()
        cursor.execute("""
        INSERT INTO users (email, password_hash, name, role, package_id, custom_monthly_quota, recurring_credits, topup_credits, status, billing_cycle_day, last_billing_reset, api_key, created_at, updated_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (
            'admin@ejal.email',
            admin_pass_hash,
            'Master Administrator',
            'admin',
            'business',
            None,
            20000,
            5000,
            'active',
            1,
            now_iso,
            admin_api_key,
            now_iso,
            now_iso
        ))

        # Seed Demo Pro User
        demo_pass_hash = hash_password("Demo@User2026!")
        demo_api_key = generate_api_key()
        cursor.execute("""
        INSERT INTO users (email, password_hash, name, role, package_id, custom_monthly_quota, recurring_credits, topup_credits, status, billing_cycle_day, last_billing_reset, api_key, created_at, updated_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (
            'demo@ejal.email',
            demo_pass_hash,
            'Enterprise Demo User',
            'user',
            'pro',
            None,
            5000,
            750,
            'active',
            1,
            now_iso,
            demo_api_key,
            now_iso,
            now_iso
        ))

        # Seed Light Starter User
        starter_pass_hash = hash_password("Starter@User2026!")
        starter_api_key = generate_api_key()
        cursor.execute("""
        INSERT INTO users (email, password_hash, name, role, package_id, custom_monthly_quota, recurring_credits, topup_credits, status, billing_cycle_day, last_billing_reset, api_key, created_at, updated_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (
            'starter@ejal.email',
            starter_pass_hash,
            'Starter Research User',
            'user',
            'starter',
            None,
            1000,
            150,
            'active',
            1,
            now_iso,
            starter_api_key,
            now_iso,
            now_iso
        ))

        conn.commit()

    conn.close()

if __name__ == '__main__':
    seed_default_users_if_needed()
    print("Auth initialized & default users seeded.")
