"""Shared discourse test helpers — admin user + API key + JSON HTTP.

The discourse recipe (compose app env) sets ONLY the DB + SMTP vars — it does NOT seed an admin user,
and Discourse's Admin API requires `Api-Key` + `Api-Username` headers (there is no auto-created API
key). So the functional tests bootstrap their own admin by running Rails inside the `app` container:
find-or-create an admin user, then create an ApiKey and print its plaintext `.key` (Discourse returns
the plaintext only at create time; it's stored hashed). Both the admin user and the key are class-B
run-scoped — they live only in the per-run app and are destroyed at teardown.

The custom tier runs on the PR HEAD. Phase prevb made the head genuinely the OFFICIAL
`discourse/discourse` image (no longer reverted to bitnamilegacy by a leaky overlay), so the Rails env
location is image-dependent — `mint_admin` detects it: OFFICIAL ships discourse at `/var/www/discourse`
(ruby/bundle on PATH) and reads the DB password from `$DISCOURSE_DB_PASSWORD`, which the recipe's
entrypoint exports from `/run/secrets/db_password` only for the BOOT process — a fresh `docker exec`
must re-export it. The legacy `bitnamilegacy/discourse` ships at `/opt/bitnami/discourse` (ruby at
`/opt/bitnami/ruby/bin`, DB password injected via env). The probe tries official first, bitnami second.

`mint_admin(domain)` returns (api_key, api_username). Each call creates a fresh ApiKey (cheap;
idempotent enough — the shared deployment's functional tests each mint their own), reusing the same
admin user across calls. Uses `lifecycle.exec_in_app` (hardened exec with retry).
"""

from __future__ import annotations

import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "runner"))
from harness import lifecycle  # noqa: E402

# Rails snippet (single line): find-or-create an admin, create an ApiKey, print key + username as the
# last two lines. SecureRandom is available in the Rails runtime. We mark the user active + approved
# so the API accepts it. created_by_id must be set (ApiKey validates it).
#
# We also enable `allow_uncategorized_topics` (a standard Discourse feature, off by default since 3.x):
# without it, POST /posts.json with no category 422s "Category can't be blank". This is config parity
# with a real forum (the operator would either enable uncategorized or pick a category), not a test
# weakening — the create-topic round-trip still posts a real topic and asserts a unique marker survives.
_BOOTSTRAP_RB = (
    "SiteSetting.allow_uncategorized_topics = true; "
    "u = User.where(admin: true).order(:id).first; "
    "if u.nil?; "
    "u = User.create!(username: 'ccciadmin', name: 'CCCI Admin', "
    "email: 'ccciadmin@ccci.example.com', password: SecureRandom.hex(20), "
    "active: true, approved: true, trust_level: 1); "
    "u.update!(admin: true); u.activate; "
    "end; "
    "k = ApiKey.create!(description: 'ccci-run', created_by_id: u.id); "
    "puts 'CCCI_API_KEY=' + k.key; "
    "puts 'CCCI_API_USER=' + u.username"
)


def mint_admin(domain: str) -> tuple[str, str]:
    """Bootstrap an admin + fresh API key via Rails in the app container. Returns (api_key, username).

    Image-agnostic (phase prevb): detects the OFFICIAL `discourse/discourse` layout
    (`/var/www/discourse`, ruby on PATH) vs the legacy `bitnamilegacy/discourse`
    (`/opt/bitnami/discourse`, ruby at `/opt/bitnami/ruby/bin`). On the OFFICIAL image a fresh
    `docker exec` lacks `DISCOURSE_DB_PASSWORD` (the entrypoint exports it only for the boot process),
    so re-export it from `/run/secrets/db_password` before Rails connects."""
    cmd = (
        "set -e; "
        "if [ -d /var/www/discourse ]; then "  # official discourse/discourse
        "  cd /var/www/discourse; "
        '  export DISCOURSE_DB_PASSWORD="$(cat /run/secrets/db_password 2>/dev/null)"; '
        f'  RAILS_ENV=production bin/rails runner "{_BOOTSTRAP_RB}"; '
        "elif [ -d /opt/bitnami/discourse ]; then "  # legacy bitnamilegacy/discourse
        "  cd /opt/bitnami/discourse; "
        "  RUBY=$(command -v ruby || echo /opt/bitnami/ruby/bin/ruby); "
        f'  RAILS_ENV=production "$RUBY" bin/rails runner "{_BOOTSTRAP_RB}"; '
        "else "
        '  echo "no discourse app dir (/var/www/discourse|/opt/bitnami/discourse)" >&2; exit 3; '
        "fi"
    )
    out = lifecycle.exec_in_app(domain, ["bash", "-c", cmd], service="app", timeout=240)
    key = user = None
    for line in out.splitlines():
        line = line.strip()
        if line.startswith("CCCI_API_KEY="):
            key = line.split("=", 1)[1].strip()
        elif line.startswith("CCCI_API_USER="):
            user = line.split("=", 1)[1].strip()
    assert key and user, (
        f"could not bootstrap discourse admin/API key; rails output tail:\n{out[-1000:]}"
    )
    return key, user


def admin_headers(api_key: str, api_username: str) -> dict[str, str]:
    return {"Api-Key": api_key, "Api-Username": api_username}
