from __future__ import annotations

from typing import Any
from urllib.parse import quote

from flask import jsonify, g, request

from .. import ariadne_client, settings
from ..db import connect, configured
from ..keycloak import admin_client, require_auth, require_portal_admin
from ..provisioning import provision_access_request


def register(app) -> None:
    """Register administrator routes for access-request decisions."""

    @app.route("/api/admin/access/requests", methods=["GET"])
    @require_auth
    def admin_list_requests() -> Any:
        """List pending access requests for portal administrators."""

        ok, resp = require_portal_admin()
        if not ok:
            return resp
        if not configured():
            return jsonify({"error": "server not configured"}), 503
        if ariadne_client.enabled():
            return ariadne_client.proxy("GET", "/api/admin/access/requests")

        try:
            with connect() as conn:
                rows = conn.execute(
                    """
                    SELECT request_code, username, contact_email, first_name, last_name, note, status, created_at
                    FROM access_requests
                    WHERE status = 'pending'
                    ORDER BY created_at ASC
                    LIMIT 200
                    """
                ).fetchall()
        except Exception:
            return jsonify({"error": "failed to load requests"}), 502

        output: list[dict[str, Any]] = []
        for row in rows:
            output.append(
                {
                    "id": row["request_code"],
                    "username": row["username"],
                    "email": row.get("contact_email") or "",
                    "first_name": row.get("first_name") or "",
                    "last_name": row.get("last_name") or "",
                    "request_code": row["request_code"],
                    "created_at": (row.get("created_at").isoformat() if row.get("created_at") else ""),
                    "note": row.get("note") or "",
                }
            )
        return jsonify({"requests": output})

    @app.route("/api/admin/access/flags", methods=["GET"])
    @require_auth
    def admin_list_flags() -> Any:
        """List Keycloak groups that can be applied as approval flags."""

        ok, resp = require_portal_admin()
        if not ok:
            return resp
        if ariadne_client.enabled():
            return ariadne_client.proxy("GET", "/api/admin/access/flags")
        if not admin_client().ready():
            return jsonify({"error": "keycloak admin unavailable"}), 503
        try:
            groups = admin_client().list_group_names()
        except Exception:
            return jsonify({"error": "failed to list flags"}), 502
        excluded = set(settings.PORTAL_ADMIN_GROUPS)
        flags = sorted([name for name in groups if name not in excluded])
        return jsonify({"flags": flags})

    @app.route("/api/admin/access/requests/<username>/approve", methods=["POST"])
    @require_auth
    def admin_approve_request(username: str) -> Any:
        """Approve one verified access request and start provisioning.

        WHY: approval should atomically record the admin decision before
        best-effort provisioning so status polling can surface any later issue.
        """

        ok, resp = require_portal_admin()
        if not ok:
            return resp
        if not configured():
            return jsonify({"error": "server not configured"}), 503
        payload = request.get_json(silent=True) or {}
        if ariadne_client.enabled():
            return ariadne_client.proxy(
                "POST",
                f"/api/admin/access/requests/{quote(username, safe='')}/approve",
                payload=payload,
            )

        decided_by = getattr(g, "keycloak_username", "") or ""
        flags_raw = payload.get("flags") if isinstance(payload, dict) else None
        flags = [f for f in flags_raw if isinstance(f, str)] if isinstance(flags_raw, list) else []
        note = payload.get("note") if isinstance(payload, dict) else None
        note = str(note).strip() if isinstance(note, str) else None
        try:
            with connect() as conn:
                row = conn.execute(
                    """
                    UPDATE access_requests
                    SET status = 'accounts_building',
                        decided_at = NOW(),
                        decided_by = %s,
                        approval_flags = %s,
                        approval_note = %s
                    WHERE username = %s AND status = 'pending'
                      AND email_verified_at IS NOT NULL
                    RETURNING request_code
                    """,
                    (decided_by or None, flags or None, note, username),
                ).fetchone()
        except Exception:
            return jsonify({"error": "failed to approve request"}), 502

        if not row:
            return jsonify({"ok": True, "request_code": ""})

        # Provision the account best-effort (Keycloak user + Mailu password + sync).
        try:
            provision_access_request(row["request_code"])
        except Exception:
            # Keep the request in accounts_building; status checks will surface it.
            pass
        return jsonify({"ok": True, "request_code": row["request_code"]})

    @app.route("/api/admin/access/requests/<username>/deny", methods=["POST"])
    @require_auth
    def admin_deny_request(username: str) -> Any:
        """Deny one pending access request with optional admin context."""

        ok, resp = require_portal_admin()
        if not ok:
            return resp
        if not configured():
            return jsonify({"error": "server not configured"}), 503
        payload = request.get_json(silent=True) or {}
        if ariadne_client.enabled():
            return ariadne_client.proxy(
                "POST",
                f"/api/admin/access/requests/{quote(username, safe='')}/deny",
                payload=payload,
            )

        decided_by = getattr(g, "keycloak_username", "") or ""
        note = payload.get("note") if isinstance(payload, dict) else None
        note = str(note).strip() if isinstance(note, str) else None
        try:
            with connect() as conn:
                row = conn.execute(
                    """
                    UPDATE access_requests
                    SET status = 'denied',
                        decided_at = NOW(),
                        decided_by = %s,
                        denial_note = %s
                    WHERE username = %s AND status = 'pending'
                    RETURNING request_code
                    """,
                    (decided_by or None, note, username),
                ).fetchone()
        except Exception:
            return jsonify({"error": "failed to deny request"}), 502

        if not row:
            return jsonify({"ok": True, "request_code": ""})
        return jsonify({"ok": True, "request_code": row["request_code"]})
