"""Routes: admin auth"""
import hmac
import secrets
import time
from flask import abort, flash, jsonify, redirect, render_template, request, session, url_for
from werkzeug.security import check_password_hash, generate_password_hash

from nexora.config import ADMIN_PASSWORD, ADMIN_USERNAME, logger
from nexora.core import app
from nexora.database.connection import execute, query
from nexora.security.rate_limit import _login_limiter
from nexora.security.csrf import validate_csrf_token
from nexora.security.permissions import admin_required


# â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
# Admin auth
# â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
@app.route("/admin", methods=["GET", "POST"])
def admin_login():
    if session.get("admin"):
        return redirect(url_for("admin_dashboard"))

    # CSRF check for login POST (exempted from before_request)
    if request.method == "POST" and not validate_csrf_token():
        abort(403)

    if request.method == "POST":
        username = request.form.get("username", "").strip()
        password = request.form.get("password", "")

        # Rate limiting: lock out after 5 failures for 15 minutes
        client_key = f"login:{request.remote_addr}:{username}"
        if _login_limiter.is_locked(client_key):
            flash("Too many failed attempts. Please try again in 15 minutes.", "danger")
            return render_template("admin_login.html"), 429

        authenticated = False
        is_root = False

        # Check root admin: env password
        if username == ADMIN_USERNAME and ADMIN_PASSWORD and password == ADMIN_PASSWORD:
            authenticated = True
            is_root = True

        # Check root admin: hashed password in DB
        if not authenticated and username == ADMIN_USERNAME:
            settings_row = query("SELECT admin_password_hash FROM settings WHERE id = 1", one=True)
            if settings_row and settings_row["admin_password_hash"] and check_password_hash(settings_row["admin_password_hash"], password):
                authenticated = True
                is_root = True

        # Check staff users
        if not authenticated:
            staff = query("SELECT * FROM admin_users WHERE username = ? AND active = 1", (username,), one=True)
            if staff and check_password_hash(staff["password_hash"], password):
                authenticated = True

        if not authenticated:
            remaining = _login_limiter.record_failure(client_key)
            if remaining:
                logger.warning("Login lockout for %s (IP %s) — %d seconds", username, request.remote_addr, remaining)
            flash("Invalid username or password.", "danger")
            return render_template("admin_login.html")

        # Success: regenerate session to prevent fixation
        session.clear()
        session["admin"] = True
        session["admin_username"] = username if not is_root else ADMIN_USERNAME
        if not is_root:
            staff = query("SELECT * FROM admin_users WHERE username = ? AND active = 1", (username,), one=True)
            if staff:
                session["role_id"] = staff["role_id"]
                session["admin_user_id"] = staff["id"]
        session["_login_time"] = str(time.time())
        session.permanent = True
        # Generate a fresh CSRF token for the new session
        session["_csrf_token"] = secrets.token_hex(32)

        _login_limiter.clear(client_key)
        logger.info("Admin login: %s from %s", username, request.remote_addr)
        return redirect(url_for("admin_dashboard"))

    return render_template("admin_login.html")


@app.route("/admin/logout", methods=["POST"])
def admin_logout():
    if not validate_csrf_token():
        abort(403)
    username = session.get("admin_username", "unknown")
    session.clear()
    logger.info("Admin logout: %s from %s", username, request.remote_addr)
    flash("Logged out successfully.", "success")
    return redirect(url_for("admin_login"))


# â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
# Admin pages
# â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
@app.route("/admin/heartbeat", methods=["POST"])
@admin_required
def admin_heartbeat():
    """AJAX heartbeat — keeps the session alive and returns remaining seconds."""
    if not validate_csrf_token():
        abort(403)
    try:
        login_ts = float(session.get("_login_time", 0))
        remaining = app.config["PERMANENT_SESSION_LIFETIME"].total_seconds() - (time.time() - login_ts)
    except (ValueError, TypeError):
        remaining = 0
    return jsonify({"ok": True, "expires_in": max(0, int(remaining))})


@app.route("/admin/change-password", methods=["POST"])
@admin_required
def admin_change_password():
    current_password = request.form.get("current_password", "")
    new_password = request.form.get("new_password", "")
    confirm_password = request.form.get("confirm_password", "")

    if not current_password or not new_password or not confirm_password:
        flash("All password fields are required.", "danger")
        return redirect(url_for("admin_settings"))

    if len(new_password) < 8:
        flash("New password must be at least 8 characters.", "danger")
        return redirect(url_for("admin_settings"))

    if new_password != confirm_password:
        flash("New password and confirmation do not match.", "danger")
        return redirect(url_for("admin_settings"))

    is_root = session.get("admin_username") == ADMIN_USERNAME

    if is_root:
        settings_row = query("SELECT admin_password_hash FROM settings WHERE id = 1", one=True)
        stored_hash = settings_row["admin_password_hash"] if settings_row else ""
        # Once a hash is set, the plaintext fallback no longer works
        if stored_hash:
            password_ok = check_password_hash(stored_hash, current_password)
        else:
            password_ok = bool(ADMIN_PASSWORD) and hmac.compare_digest(current_password, ADMIN_PASSWORD)
            if password_ok:
                # Migrate to hashed: set the hash so plaintext never works again
                execute("UPDATE settings SET admin_password_hash = ? WHERE id = 1", (generate_password_hash(new_password),))
                flash("Password changed. Root admin password is now stored securely.", "success")
                return redirect(url_for("admin_settings"))
        if not password_ok:
            flash("Current password is incorrect.", "danger")
            return redirect(url_for("admin_settings"))
        execute("UPDATE settings SET admin_password_hash = ? WHERE id = 1", (generate_password_hash(new_password),))
    else:
        staff = query("SELECT password_hash FROM admin_users WHERE username = ?", (session.get("admin_username", ""),), one=True)
        if not staff or not check_password_hash(staff["password_hash"], current_password):
            flash("Current password is incorrect.", "danger")
            return redirect(url_for("admin_settings"))
        execute("UPDATE admin_users SET password_hash = ? WHERE username = ?", (generate_password_hash(new_password), session["admin_username"]))

    flash("Password changed successfully.", "success")
    return redirect(url_for("admin_settings"))
