"""Username selection flow.

Users must choose a preferred_username before they can publish.
This creates their ActorProfile with RSA keys.
"""
from __future__ import annotations

import re

from quart import (
    Blueprint, request, redirect, url_for, g, abort,
)

from shared.services.registry import services


# Username rules: 3-32 chars, lowercase alphanumeric + underscores
USERNAME_RE = re.compile(r"^[a-z][a-z0-9_]{2,31}$")

# Reserved usernames
RESERVED = frozenset({
    "admin", "administrator", "root", "system", "moderator", "mod",
    "support", "help", "info", "postmaster", "webmaster", "abuse",
    "federation", "activitypub", "api", "static", "media", "assets",
    "well-known", "nodeinfo", "inbox", "outbox", "followers", "following",
})


async def _render_choose_username(*, actor=None, error="", username=""):
    """Render choose-username page — replaces sx_components helper."""
    from shared.browser.app.csrf import generate_csrf_token
    from shared.config import config
    from shared.sx.helpers import sx_call
    from shared.sx.page import get_template_context
    from sxc.pages.utils import _social_page
    from markupsafe import escape

    ctx = await get_template_context()
    csrf = generate_csrf_token()
    ap_domain = config().get("ap_domain", "rose-ash.com")
    check_url = url_for("identity.check_username")

    error_sx = sx_call("auth-error-banner", error=error) if error else ""
    content = sx_call(
        "federation-choose-username",
        domain=str(escape(ap_domain)),
        error=error_sx or None,
        csrf=csrf, username=str(escape(username)),
        check_url=check_url,
    )
    return await _social_page(ctx, actor, content=content,
                        title="Choose Username \u2014 Rose Ash")


def register(url_prefix="/identity"):
    bp = Blueprint("identity", __name__, url_prefix=url_prefix)

    @bp.get("/choose-username")
    async def choose_username_form():
        if not g.get("user"):
            return redirect(url_for("auth.login_form"))

        # Already has a username?
        actor = await services.federation.get_actor_by_user_id(g.s, g.user.id)
        if actor:
            return redirect(url_for("activitypub.actor_profile", username=actor.preferred_username))

        return await _render_choose_username(actor=actor)

    @bp.post("/choose-username")
    async def choose_username():
        if not g.get("user"):
            abort(401)

        # Already has a username?
        existing = await services.federation.get_actor_by_user_id(g.s, g.user.id)
        if existing:
            return redirect(url_for("activitypub.actor_profile", username=existing.preferred_username))

        form = await request.form
        username = (form.get("username") or "").strip().lower()

        # Validate format
        error = None
        if not USERNAME_RE.match(username):
            error = (
                "Username must be 3-32 characters, start with a letter, "
                "and contain only lowercase letters, numbers, and underscores."
            )
        elif username in RESERVED:
            error = "This username is reserved."
        elif not await services.federation.username_available(g.s, username):
            error = "This username is already taken."

        if error:
            return await _render_choose_username(error=error, username=username), 400

        # Create ActorProfile with RSA keys
        display_name = g.user.name or username
        actor = await services.federation.create_actor(
            g.s, g.user.id, username,
            display_name=display_name,
        )

        # Redirect to where they were going, or their new profile
        next_url = request.args.get("next")
        if next_url:
            return redirect(next_url)
        return redirect(url_for("activitypub.actor_profile", username=actor.preferred_username))

    @bp.get("/check-username")
    async def check_username():
        """HTMX endpoint to check username availability."""
        username = (request.args.get("username") or "").strip().lower()

        if not username:
            return ""

        if not USERNAME_RE.match(username):
            return '<span class="text-red-600">Invalid format</span>'

        if username in RESERVED:
            return '<span class="text-red-600">Reserved</span>'

        available = await services.federation.username_available(g.s, username)
        if available:
            return '<span class="text-green-600">Available</span>'
        return '<span class="text-red-600">Taken</span>'

    return bp
