#!/usr/bin/env python3
"""CLI: issue platform invitation tokens that gate public sign-up.

Usage:
  uv run scripts/issue_invite.py --email founder@example.com
  uv run scripts/issue_invite.py --count 3 --expires 14d
"""

import argparse
import asyncio
import re
import secrets
import sys
import uuid
from datetime import UTC, datetime, timedelta

from app.config import get_settings
from app.db.session import async_session_factory
from app.models.invitation_token import InvitationToken

# Force-import the User SQLModel so the metadata can resolve the
# `invitation_token.used_by → user.id` foreign key at flush time.
# `app.models/__init__.py` intentionally omits the auth-mirror tables.
from app.models.user import User  # noqa: F401

_DURATION_RE = re.compile(r"(\d+)([dhm])")


def _parse_duration(raw: str) -> timedelta:
    match = _DURATION_RE.fullmatch(raw)
    if not match:
        raise argparse.ArgumentTypeError(f"Bad duration {raw!r}; expected like 7d, 24h, 30m")

    amount = int(match.group(1))
    unit = match.group(2)
    if unit == "d":
        return timedelta(days=amount)
    if unit == "h":
        return timedelta(hours=amount)
    return timedelta(minutes=amount)


async def _issue(email: str | None, expires_in: timedelta, count: int) -> list[str]:
    settings = get_settings()
    frontend = settings.BETTER_AUTH_URL.rstrip("/")
    urls: list[str] = []
    now = datetime.now(UTC)

    async with async_session_factory() as session:
        for _ in range(count):
            token = secrets.token_urlsafe(32)
            session.add(
                InvitationToken(
                    id=uuid.uuid4(),
                    token=token,
                    email=email,
                    created_by=None,
                    used_by=None,
                    used_at=None,
                    expires_at=now + expires_in,
                    created_at=now,
                )
            )
            urls.append(f"{frontend}/sign-up?token={token}")

        await session.commit()

    return urls


def main() -> int:
    parser = argparse.ArgumentParser(description="Issue platform invitation tokens.")
    parser.add_argument(
        "--email",
        default=None,
        help="Pre-bind the token to this email (optional).",
    )
    parser.add_argument(
        "--expires",
        type=_parse_duration,
        default=timedelta(days=7),
        help="Expiration window in Nd, Nh, or Nm format (default: 7d).",
    )
    parser.add_argument(
        "--count",
        type=int,
        default=1,
        help="Number of tokens to issue (1-50).",
    )
    args = parser.parse_args()

    if not 1 <= args.count <= 50:
        print("--count must be 1..50", file=sys.stderr)
        return 2

    email = args.email.lower().strip() if args.email else None
    urls = asyncio.run(_issue(email, args.expires, args.count))
    for url in urls:
        print(url)
    return 0


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