#!/usr/bin/env python3
"""
Promote/demote a user's global role in athena-auth.

Examples:
  python scripts/set_global_admin.py --email you@example.com
  python scripts/set_global_admin.py --user-id 123 --role user
  python scripts/set_global_admin.py --email you@example.com --dry-run
"""

from __future__ import annotations

import argparse
import os
import shutil
import subprocess
import sys


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description=(
            "Set a user's global role in the users table. "
            "Defaults to role='admin'."
        )
    )
    target = parser.add_mutually_exclusive_group(required=True)
    target.add_argument("--email", help="Target user email")
    target.add_argument("--user-id", help="Target user id")
    parser.add_argument(
        "--role",
        default="admin",
        help="Role to set (default: admin)",
    )
    parser.add_argument(
        "--database-url",
        default=os.environ.get("DATABASE_URL"),
        help="PostgreSQL connection URL (defaults to env DATABASE_URL)",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Print SQL and exit without executing",
    )
    return parser


def ensure_prerequisites(database_url: str | None) -> str:
    if not database_url:
        raise ValueError(
            "DATABASE_URL is not set. Pass --database-url or export DATABASE_URL first."
        )
    psql_path = shutil.which("psql")
    if not psql_path:
        raise RuntimeError(
            "psql was not found in PATH. Install PostgreSQL client tools and retry."
        )
    try:
        version_result = subprocess.run(
            [psql_path, "--version"],
            check=False,
            capture_output=True,
            text=True,
            timeout=5,
        )
    except OSError as error:
        raise RuntimeError(f"psql exists but could not be executed: {error}") from error

    if version_result.returncode != 0:
        error_text = (version_result.stderr or version_result.stdout).strip()
        raise RuntimeError(
            f"psql is in PATH but failed to run (`psql --version`): {error_text or 'unknown error'}"
        )
    return database_url


def run_psql(database_url: str, sql: str, variables: dict[str, str]) -> subprocess.CompletedProcess[str]:
    command = [database_url, "-v", "ON_ERROR_STOP=1"]
    for key, value in variables.items():
        command.extend(["-v", f"{key}={value}"])
    command.extend(["-c", sql])
    return subprocess.run(
        ["psql", *command],
        check=False,
        capture_output=True,
        text=True,
    )


def main() -> int:
    parser = build_parser()
    args = parser.parse_args()

    try:
        database_url = ensure_prerequisites(args.database_url)
    except (ValueError, RuntimeError) as error:
        print(f"Error: {error}", file=sys.stderr)
        return 1

    where_clause = "email = :'email'" if args.email else "id = :'user_id'"
    variables: dict[str, str] = {"role": args.role}
    if args.email:
        variables["email"] = args.email
    else:
        variables["user_id"] = args.user_id

    sql = (
        "UPDATE users "
        "SET role = :'role' "
        f"WHERE {where_clause} "
        "RETURNING id, email, role;"
    )

    if args.dry_run:
        print("Dry run mode: no changes applied.")
        print("SQL:")
        print(sql)
        print("Variables:")
        for key, value in variables.items():
            print(f"  {key}={value}")
        return 0

    result = run_psql(database_url, sql, variables)
    if result.returncode != 0:
        if result.stdout.strip():
            print(result.stdout.strip(), file=sys.stderr)
        if result.stderr.strip():
            print(result.stderr.strip(), file=sys.stderr)
        return result.returncode

    stdout = result.stdout.strip()
    print(stdout)

    if "UPDATE 0" in stdout:
        print("No matching user found. Nothing updated.", file=sys.stderr)
        return 2

    print("Role updated successfully.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
