"""Authentication blueprint — login, signup, logout."""
from __future__ import annotations
import json, logging, os, random, sys, time
from flask import Blueprint, jsonify, redirect, render_template, request, session, url_for
import extensions as ext
from helpers import _sanitize_sales_key
import services.rbac_service as rbac

logger = logging.getLogger("apex")
auth_bp = Blueprint("auth", __name__)
_REDIS_EXPIRY = int(os.getenv("REDIS_EXPIRY", "86400"))
_DEBUG = os.getenv("FLASK_DEBUG", "false").lower() == "true"

def _send_otp_email(email, otp, purpose="login"):
    """Send OTP via email — stub if email service not configured."""
    try:
        import smtplib
        from email.mime.text import MIMEText
        smtp_host = os.getenv("SMTP_HOST", "")
        smtp_user = os.getenv("SMTP_USER", "")
        smtp_pass = os.getenv("SMTP_PASS", "")
        if not smtp_host: return False
        msg = MIMEText(f"Your APEX OTP for {purpose}: {otp}\n\nValid for 5 minutes.")
        msg["Subject"] = f"APEX OTP — {otp}"
        msg["From"] = smtp_user
        msg["To"] = email
        with smtplib.SMTP_SSL(smtp_host, 465) as s:
            s.login(smtp_user, smtp_pass)
            s.sendmail(smtp_user, [email], msg.as_string())
        return True
    except Exception as e:
        logger.warning("[OTP Email] Failed: %s", e)
        return False

def _start_session(user):
    session.permanent = False
    session["user"] = user.email
    session["user_type"] = "authenticated"
    session["sales_tenant_id"] = f"user-{_sanitize_sales_key(user.email)}"
    session["sales_workspace_label"] = user.name or user.email
    session["sales_role"] = user.role
    session["user_name"] = user.name
    session["user_dept"] = user.department
    cached = ext.redis_client.get(user.email)
    if cached:
        try:
            desig = json.loads(cached).get("DESIGNATIONNAME", [])
            if desig: session["designation"] = desig[0]
        except Exception: pass
    try:
        from services.user_service import start_authenticated_user_bootstrap
        start_authenticated_user_bootstrap(user.email)
    except Exception: pass

def _rate_limit(key, wait=60):
    last = session.get(key, 0)
    if time.time() - last < wait: return int(wait - (time.time() - last))
    return 0

def _make_otp(): return str(random.randint(100000, 999999))

def _otp_send(email, otp, purpose, session_key):
    session.update({f"{session_key}_otp": otp, f"{session_key}_otp_email": email,
                    f"{session_key}_otp_time": time.time(), f"{session_key}_otp_last_sent": time.time()})
    emailed = _send_otp_email(email, otp, purpose)
    if _DEBUG: print(f"  [{purpose.upper()} OTP] {email}: {otp}", file=sys.stderr, flush=True)
    return emailed

def _otp_verify(email, otp, session_key):
    stored = session.get(f"{session_key}_otp")
    stored_email = session.get(f"{session_key}_otp_email")
    otp_time = session.get(f"{session_key}_otp_time", 0)
    if not all([stored, stored_email, otp_time]): return False, "OTP session expired."
    if email != stored_email: return False, "Email mismatch."
    if time.time() - otp_time > 300:
        for k in [f"{session_key}_otp", f"{session_key}_otp_email", f"{session_key}_otp_time", f"{session_key}_otp_last_sent"]: session.pop(k, None)
        return False, "OTP expired."
    if otp != stored: return False, "Incorrect OTP."
    for k in [f"{session_key}_otp", f"{session_key}_otp_email", f"{session_key}_otp_time", f"{session_key}_otp_last_sent"]: session.pop(k, None)
    return True, "OK"

# ── Routes ─────────────────────────────────────────────────────────────────────
@auth_bp.route("/login", methods=["GET", "POST"])
def login():
    if "user" in session and session.get("user_type") in ("authenticated", "guest"):
        return redirect(url_for("home.index"))
    if request.method == "POST":
        if request.form.get("login_type") == "guest":
            import uuid; t = uuid.uuid4().hex[:10]
            session.update({"user":"guest","user_type":"guest","sales_guest_token":t,
                            "sales_tenant_id":f"guest-{t}","sales_workspace_label":f"Guest {t[:6]}","sales_role":"guest"})
            return redirect(url_for("home.index"))
        email = request.form.get("email","").strip().lower()
        password = request.form.get("password","")
        ok, msg, user = rbac.authenticate(email, password)
        if not ok: return render_template("login.html", error=msg)
        _start_session(user)
        return redirect(url_for("home.index"))
    return render_template("login.html")

@auth_bp.route("/verify-password", methods=["POST"])
def verify_password():
    data = request.get_json() or {}
    ok, msg, user = rbac.authenticate(data.get("email","").strip().lower(), data.get("password",""))
    if not ok: return jsonify({"success":False,"message":msg})
    return jsonify({"success":True,"name":user.name,"email":user.email})

@auth_bp.route("/login/send-otp", methods=["POST"])
def login_send_otp():
    data = request.get_json() or {}
    email = data.get("email","").strip().lower()
    ok, msg, user = rbac.authenticate(email, data.get("password",""))
    if not ok: return jsonify({"success":False,"message":msg})
    if w := _rate_limit("login_otp_last_sent"): return jsonify({"success":False,"message":f"Wait {w}s."})
    otp = _make_otp(); emailed = _otp_send(email, otp, "login", "login")
    return jsonify({"success":True,"message":"OTP sent." if emailed else "OTP generated (check terminal)."})

@auth_bp.route("/login/verify-otp", methods=["POST"])
def login_verify_otp():
    data = request.get_json() or {}
    email = data.get("email","").strip().lower()
    ok, msg = _otp_verify(email, data.get("otp","").strip(), "login")
    if not ok: return jsonify({"success":False,"message":msg})
    user = rbac.get_user(email)
    if not user: return jsonify({"success":False,"message":"User not found."})
    _start_session(user)
    return jsonify({"success":True,"message":"Login successful."})

@auth_bp.route("/stack-login/send-otp", methods=["POST"])
def stack_login_send_otp():
    data = request.get_json() or {}
    email = data.get("email","").strip().lower()
    if not email: return jsonify({"success":False,"message":"Email required."})
    if not email.endswith("@rptechindia.com"): return jsonify({"success":False,"message":"Unauthorized domain."})
    if w := _rate_limit("login_otp_last_sent"): return jsonify({"success":False,"message":f"Wait {w}s."})
    otp = _make_otp(); emailed = _otp_send(email, otp, "login", "login")
    return jsonify({"success":True,"message":"OTP sent." if emailed else "OTP generated (check terminal)."})

@auth_bp.route("/stack-login/verify-otp", methods=["POST"])
def stack_login_verify_otp():
    data = request.get_json() or {}
    email = data.get("email","").strip().lower()
    ok, msg = _otp_verify(email, data.get("otp","").strip(), "login")
    if not ok: return jsonify({"success":False,"message":msg})
    user = rbac.get_user(email)
    if not user: rbac.create_user(email, email.split("@")[0], ""); user = rbac.get_user(email)
    if not user: return jsonify({"success":False,"message":"User setup failed."})
    _start_session(user)
    return jsonify({"success":True,"message":"Login successful."})

@auth_bp.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("auth.login"))

@auth_bp.route("/signup", methods=["GET"])
def signup():
    return render_template("signup.html")

@auth_bp.route("/signup/send-otp", methods=["POST"])
def signup_send_otp():
    data = request.get_json() or {}
    email = data.get("email","").strip().lower()
    if not email: return jsonify({"success":False,"message":"Email required."})
    if w := _rate_limit("signup_otp_last_sent"): return jsonify({"success":False,"message":f"Wait {w}s."})
    existing = rbac.get_user(email)
    if existing:
        msgs = {"active":"Account exists.","pending":"Pending approval.","rejected":"Request rejected."}
        if existing.status in msgs: return jsonify({"success":False,"message":msgs[existing.status]})
    otp = _make_otp(); emailed = _otp_send(email, otp, "signup", "signup")
    return jsonify({"success":True,"message":"OTP sent." if emailed else "OTP generated (check terminal)."})

@auth_bp.route("/signup/verify", methods=["POST"])
def signup_verify():
    data = request.get_json() or {}
    email = data.get("email","").strip().lower()
    ok, msg = _otp_verify(email, data.get("otp","").strip(), "signup")
    if not ok: return jsonify({"success":False,"message":msg})
    name = data.get("name","").strip(); password = data.get("password","").strip()
    if not name: return jsonify({"success":False,"message":"Name required."})
    if not password or len(password) < 8: return jsonify({"success":False,"message":"Password min 8 chars."})
    ok, msg = rbac.create_user(email, name, data.get("department","").strip(), password)
    return jsonify({"success":ok,"message":msg})

@auth_bp.route("/signup/brands")
def signup_brands():
    from data.master_data import brands_as_options
    return jsonify(brands_as_options())

@auth_bp.route("/set-password/<token>", methods=["GET"])
def set_password_page(token):
    record = rbac.get_user_by_set_password_token(token)
    if not record: return render_template("set_password.html", token=token, expired=True, user_name="")
    return render_template("set_password.html", token=token, expired=time.time()>(record.get("set_password_token_expiry") or 0), user_name=record.get("name",""))

@auth_bp.route("/set-password/<token>", methods=["POST"])
def set_password_submit(token):
    data = request.get_json() or {}
    password, confirm = data.get("password",""), data.get("confirm","")
    if not password or len(password) < 8: return jsonify({"success":False,"message":"Min 8 chars."})
    if password != confirm: return jsonify({"success":False,"message":"Passwords don't match."})
    ok, msg = rbac.set_password_via_token(token, password)
    if ok: session.clear()
    return jsonify({"success":ok,"message":msg})

@auth_bp.route("/send_otp", methods=["POST"])
def send_otp():
    data = request.get_json() or {}
    email = data.get("email","").strip()
    if not email: return jsonify({"success":False,"message":"Email required."})
    if w := _rate_limit("otp_last_sent"): return jsonify({"success":False,"message":f"Wait {w}s."})
    otp = _make_otp(); emailed = _otp_send(email, otp, "login", "otp")
    return jsonify({"success":True,"message":"OTP sent." if emailed else "OTP generated (check terminal)."})

@auth_bp.route("/api/me")
def me():
    email = session.get("user")
    if not email: return jsonify({"error":"Not authenticated"}), 401
    user = rbac.get_user(email)
    if not user: return jsonify({"email":email,"role":session.get("sales_role","employee")})
    return jsonify({"email":user.email,"full_name":user.name,"role":user.role,"department":user.department,"status":user.status})

@auth_bp.route("/api/me/permissions")
def me_permissions():
    email = session.get("user")
    if not email or session.get("user_type") != "authenticated": return jsonify({"permissions":[],"role":"guest"})
    return jsonify({"permissions":rbac.get_user_effective_permissions(email),"role":session.get("sales_role","employee"),"email":email})

@auth_bp.route("/get_kpi")
def get_kpi():
    email = session.get("user")
    if not email or session.get("user_type") != "authenticated": return jsonify({})
    try:
        from cells import fetch_cells_data
        from services.user_service import get_jwt_token, get_user_company_data, get_user_where_clause
        data = ext.redis_client.get(f"kpi:{email}")
        if not data:
            if not ext.redis_client.get(email):
                ext.redis_client.set(email, json.dumps(get_user_company_data(email, jwt_token=get_jwt_token(email))), ex=_REDIS_EXPIRY)
            kpi_data = fetch_cells_data(email, external_where=get_user_where_clause(email))
            ext.redis_client.set(f"kpi:{email}", json.dumps(kpi_data), ex=_REDIS_EXPIRY)
            data = json.dumps(kpi_data)
        return jsonify(json.loads(data))
    except Exception as e:
        logger.error("[KPI] %s: %s", email, e); return jsonify({})

@auth_bp.route("/get_designations")
def get_designations():
    email = session.get("user")
    if not email: return jsonify({"status":"error","designations":[]})
    try:
        user_data = ext.redis_client.get(email)
        if not user_data:
            from services.user_service import get_jwt_token, get_user_company_data
            d = get_user_company_data(email, jwt_token=get_jwt_token(email))
            ext.redis_client.set(email, json.dumps(d), ex=_REDIS_EXPIRY)
            return jsonify({"status":"success","designations":d.get("DESIGNATIONNAME",[])})
        parsed = json.loads(user_data if isinstance(user_data, str) else user_data.decode())
        return jsonify({"status":"success","designations":parsed.get("DESIGNATIONNAME",[])})
    except Exception as e:
        return jsonify({"status":"error","message":str(e)})

@auth_bp.route("/get_source_settings")
def get_source_settings():
    email = session.get("user")
    if not email: return jsonify({"dashboard":False})
    try: return jsonify(json.loads(ext.redis_client.get(f"src:{email}") or "{}"))
    except Exception: return jsonify({"dashboard":False})

@auth_bp.route("/get_user_details")
def get_user_details():
    if not session.get("user") or session.get("user_type") != "authenticated": return jsonify({"error":"Unauthorized"}), 401
    email = request.args.get("email")
    if session.get("sales_role") not in ("admin",) and email != session.get("user"): return jsonify({"error":"Forbidden"}), 403
    data = ext.redis_client.get(email)
    if not data: return jsonify({"error":"No data"}), 404
    try: return jsonify(json.loads(data))
    except: return jsonify({"raw":str(data)})