# passenger_wsgi.py
# Python 3.11 / cPanel Passenger WSGI
# Safe starter: PHP -> Python JSON API, SQLite sessions, manual CAPTCHA workflow.
# Actual UIDAI operations require an authorized provider adapter; this file does not
# call private/internal UIDAI endpoints or automate CAPTCHA solving.

import json
import os
import re
import sqlite3
import time
import secrets
import hashlib
from urllib.parse import parse_qs

APP_ROOT = os.path.dirname(os.path.abspath(__file__))
DB_PATH = os.path.join(APP_ROOT, "uid2_sessions.sqlite3")
SESSION_TTL = 300

# Set the same random value in uid2.php as PHP_API_SECRET.
# Example: python -c "import secrets; print(secrets.token_hex(32))"
API_SECRET = "CHANGE_THIS_TO_A_LONG_RANDOM_SECRET"


def db():
    conn = sqlite3.connect(DB_PATH, timeout=10)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS sessions (
            sid TEXT PRIMARY KEY,
            state TEXT NOT NULL,
            created_at INTEGER NOT NULL,
            expires_at INTEGER NOT NULL,
            provider_ref TEXT
        )
    """)
    conn.commit()
    return conn


def response(start_response, status, payload):
    body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
    start_response(status, [
        ("Content-Type", "application/json; charset=utf-8"),
        ("Content-Length", str(len(body))),
        ("Cache-Control", "no-store"),
    ])
    return [body]


def read_json(environ):
    try:
        size = int(environ.get("CONTENT_LENGTH") or 0)
        if size < 0 or size > 65536:
            return None
        raw = environ["wsgi.input"].read(size) if size else b"{}"
        value = json.loads(raw.decode("utf-8"))
        return value if isinstance(value, dict) else None
    except Exception:
        return None


def provider_start(aadhaar_number):
    """
    Integrate your authorized UIDAI AUA/KUA/ASA provider here.
    Expected return: {"provider_ref": "...", "message": "..."}
    Do not implement CAPTCHA bypass or private endpoint automation here.
    """
    raise NotImplementedError("Authorized UIDAI provider is not configured")


def provider_submit_captcha(provider_ref, captcha_text):
    """
    Send user-entered CAPTCHA to the authorized provider.
    Expected return: {"otp_requested": True, "message": "..."}
    """
    raise NotImplementedError("Authorized UIDAI provider is not configured")


def provider_verify(provider_ref, otp):
    """
    Verify OTP through the authorized provider.
    Expected return: {"verified": True, "result": {...}}
    """
    raise NotImplementedError("Authorized UIDAI provider is not configured")


def application(environ, start_response):
    path = environ.get("PATH_INFO", "/")
    method = environ.get("REQUEST_METHOD", "GET").upper()

    if path in ("/", "") and method == "GET":
        return response(start_response, "200 OK", {
            "success": True,
            "service": "UID2 Python API",
            "python": "3.11",
            "endpoints": ["POST /api"],
            "provider_configured": False
        })

    if path != "/api":
        return response(start_response, "404 Not Found", {
            "success": False, "error": "not_found"
        })

    if method != "POST":
        return response(start_response, "405 Method Not Allowed", {
            "success": False, "error": "method_not_allowed"
        })

    data = read_json(environ)
    if data is None:
        return response(start_response, "400 Bad Request", {
            "success": False, "error": "invalid_json"
        })

    supplied = environ.get("HTTP_X_API_KEY", "")
    if not API_SECRET.startswith("CHANGE_") and not secrets.compare_digest(supplied, API_SECRET):
        return response(start_response, "401 Unauthorized", {
            "success": False, "error": "unauthorized"
        })

    action = str(data.get("action", "")).strip().lower()

    if action in ("ping", "diag"):
        return response(start_response, "200 OK", {
            "success": True,
            "message": "Python API is running",
            "provider_configured": False
        })

    if action == "start":
        uid = re.sub(r"\D", "", str(data.get("uid", "")))
        if not re.fullmatch(r"\d{12}", uid):
            return response(start_response, "400 Bad Request", {
                "success": False, "error": "invalid_uid",
                "message": "UID must contain exactly 12 digits"
            })
        try:
            result = provider_start(uid)
            sid = secrets.token_hex(16)
            now = int(time.time())
            with db() as conn:
                conn.execute(
                    "INSERT INTO sessions(sid,state,created_at,expires_at,provider_ref) VALUES(?,?,?,?,?)",
                    (sid, "captcha_pending", now, now + SESSION_TTL, str(result["provider_ref"]))
                )
            return response(start_response, "200 OK", {
                "success": True, "sid": sid,
                "message": result.get("message", "CAPTCHA step ready"),
                "expires_in": SESSION_TTL
            })
        except NotImplementedError as exc:
            return response(start_response, "501 Not Implemented", {
                "success": False, "error": "official_integration_required",
                "message": str(exc)
            })
        except Exception:
            return response(start_response, "502 Bad Gateway", {
                "success": False, "error": "provider_request_failed"
            })

    if action in ("captcha", "submit_captcha"):
        sid = str(data.get("sid", "")).strip()
        captcha = str(data.get("captcha", "")).strip()
        if not re.fullmatch(r"[A-Za-z0-9]{3,10}", captcha):
            return response(start_response, "400 Bad Request", {
                "success": False, "error": "invalid_captcha"
            })
        with db() as conn:
            row = conn.execute(
                "SELECT state, expires_at, provider_ref FROM sessions WHERE sid=?",
                (sid,)
            ).fetchone()
        if not row or row[1] < int(time.time()):
            return response(start_response, "410 Gone", {
                "success": False, "error": "session_expired"
            })
        try:
            result = provider_submit_captcha(row[2], captcha)
            with db() as conn:
                conn.execute("UPDATE sessions SET state='otp_pending' WHERE sid=?", (sid,))
            return response(start_response, "200 OK", {
                "success": True, "sid": sid,
                "message": result.get("message", "OTP request accepted")
            })
        except NotImplementedError as exc:
            return response(start_response, "501 Not Implemented", {
                "success": False, "error": "official_integration_required",
                "message": str(exc)
            })
        except Exception:
            return response(start_response, "502 Bad Gateway", {
                "success": False, "error": "provider_request_failed"
            })

    if action == "verify":
        sid = str(data.get("sid", "")).strip()
        otp = re.sub(r"\D", "", str(data.get("otp", "")))
        if not re.fullmatch(r"\d{6}", otp):
            return response(start_response, "400 Bad Request", {
                "success": False, "error": "invalid_otp"
            })
        with db() as conn:
            row = conn.execute(
                "SELECT state, expires_at, provider_ref FROM sessions WHERE sid=?",
                (sid,)
            ).fetchone()
        if not row or row[1] < int(time.time()):
            return response(start_response, "410 Gone", {
                "success": False, "error": "session_expired"
            })
        if row[0] != "otp_pending":
            return response(start_response, "409 Conflict", {
                "success": False, "error": "otp_not_requested"
            })
        try:
            result = provider_verify(row[2], otp)
            verified = bool(result.get("verified"))
            with db() as conn:
                conn.execute(
                    "UPDATE sessions SET state=? WHERE sid=?",
                    ("verified" if verified else "otp_pending", sid)
                )
            return response(start_response, "200 OK", {
                "success": verified,
                "verified": verified,
                "message": "Verification completed" if verified else "OTP verification failed",
                "result": result.get("result") if verified else None
            })
        except NotImplementedError as exc:
            return response(start_response, "501 Not Implemented", {
                "success": False, "error": "official_integration_required",
                "message": str(exc)
            })
        except Exception:
            return response(start_response, "502 Bad Gateway", {
                "success": False, "error": "provider_request_failed"
            })

    return response(start_response, "400 Bad Request", {
        "success": False, "error": "unknown_action",
        "message": "Valid actions: diag, start, captcha, verify"
    })
