# app/blueprints/oauth.py
import secrets
from flask import Blueprint, request, session, redirect, flash, url_for

from tiktok_client import build_authorize_url, exchange_code_for_token, query_creator_info
from token_store import save_token


bp = Blueprint("oauth", __name__)


def _slug(s: str) -> str:
    """
    ASCII-ish slug without extra deps (Python 3.8).
    Keeps it stable and URL-safe.
    """
    s = (s or "").strip().lower()
    # lightweight normalization: keep a-z0-9, turn everything else into '-'
    out = []
    prev_dash = False
    for ch in s:
        if ("a" <= ch <= "z") or ("0" <= ch <= "9"):
            out.append(ch)
            prev_dash = False
        else:
            if not prev_dash:
                out.append("-")
                prev_dash = True
    slug = "".join(out).strip("-")
    return slug or "account"


def build_alias_from_channel(nickname: str, open_id: str) -> str:
    """
    Human-friendly alias, but collision-resistant and stable:
      <slug(nickname)>-<last6(open_id)>
    Example: luxverbi-3fa91c
    """
    tail = (open_id or "")[-6:] or "000000"
    return f"{_slug(nickname)}-{tail}"


@bp.route("/login", endpoint="login")
def login():
    """
    /login?alias=... is still supported as an override,
    but by default we will generate a channel-based alias after OAuth callback.
    """
    alias = (request.args.get("alias") or "").strip()
    state = secrets.token_hex(16)
    session["oauth_state"] = state
    # Store a requested alias (optional). If empty, callback will generate.
    session["oauth_alias_requested"] = alias
    return redirect(build_authorize_url(state))


@bp.route("/tiktok/callback", endpoint="tiktok_callback")
def tiktok_callback():
    error = request.args.get("error")
    if error:
        flash(f"TikTok error: {error}", "error")
        return redirect(url_for("ui.app_home"))

    code = request.args.get("code")
    state = request.args.get("state")

    if not code or not state or state != session.get("oauth_state"):
        flash("Invalid OAuth state or missing code.", "error")
        return redirect(url_for("ui.app_home"))

    try:
        token_data = exchange_code_for_token(code)
        session["tiktok_token"] = token_data
        session.modified = True

        # Prefer explicit alias from /login?alias=...
        requested = (session.get("oauth_alias_requested") or "").strip()

        # Otherwise generate alias from creator nickname + open_id tail
        alias = requested
        if not alias:
            open_id = (token_data or {}).get("open_id") or ""
            try:
                creator = query_creator_info(token_data.get("access_token") or "")
                nickname = (creator or {}).get("creator_nickname") or "tiktok"
            except Exception:
                nickname = "tiktok"
            alias = build_alias_from_channel(nickname, open_id)

        # Persist for worker/cron usage
        session["oauth_alias"] = alias
        save_token(alias, token_data)

        flash(f"Successfully connected with TikTok ({alias}).", "success")
    except Exception as e:
        flash(f"Error exchanging code: {e}", "error")

    return redirect(url_for("ui.app_home"))


@bp.route("/logout", methods=["POST"], endpoint="logout")
def logout():
    session.pop("tiktok_token", None)
    session.pop("drafts", None)
    # keep oauth_alias so UI can still show which account is "selected" if you want,
    # but you can clear it too if you prefer:
    # session.pop("oauth_alias", None)
    session.pop("oauth_alias_requested", None)

    flash("Disconnected.", "success")
    return redirect(url_for("ui.app_home"))
