from __future__ import annotations

from datetime import UTC, datetime, timedelta
from unittest.mock import AsyncMock, MagicMock

import pytest

from app.models.invitation_token import InvitationToken
from scripts import issue_invite


class _SessionContext:
    def __init__(self, session: MagicMock) -> None:
        self._session = session

    async def __aenter__(self) -> MagicMock:
        return self._session

    async def __aexit__(self, *_args: object) -> bool:
        return False


def _settings() -> MagicMock:
    return MagicMock(BETTER_AUTH_URL="https://frontend.example.com/")


@pytest.mark.asyncio
async def test_invitation_token_model_preserves_fields() -> None:
    now = datetime.now(UTC)
    expires_at = now + timedelta(days=7)

    token = InvitationToken(
        token="invite-token",
        email="founder@example.com",
        created_by=None,
        used_by=None,
        used_at=None,
        expires_at=expires_at,
        created_at=now,
    )

    assert token.token == "invite-token"
    assert token.email == "founder@example.com"
    assert token.created_by is None
    assert token.used_by is None
    assert token.used_at is None
    assert token.expires_at == expires_at
    assert token.created_at == now
    assert token.id is not None


@pytest.mark.asyncio
async def test_issue_inserts_rows_and_returns_signup_urls(monkeypatch: pytest.MonkeyPatch) -> None:
    session = MagicMock()
    session.add = MagicMock()
    session.commit = AsyncMock()

    issued_tokens = iter(["token-one", "token-two"])
    fixed_now = datetime(2026, 5, 22, tzinfo=UTC)

    monkeypatch.setattr(issue_invite, "get_settings", _settings)
    monkeypatch.setattr(
        issue_invite,
        "async_session_factory",
        lambda: _SessionContext(session),
    )
    monkeypatch.setattr(issue_invite.secrets, "token_urlsafe", lambda _size: next(issued_tokens))
    monkeypatch.setattr(issue_invite.uuid, "uuid4", lambda: "generated-id")

    class _FrozenDatetime(datetime):
        @classmethod
        def now(cls, tz=None):
            return fixed_now if tz is UTC else fixed_now.replace(tzinfo=None)

    monkeypatch.setattr(issue_invite, "datetime", _FrozenDatetime)

    urls = await issue_invite._issue(
        email="founder@example.com",
        expires_in=timedelta(days=3),
        count=2,
    )

    assert urls == [
        "https://frontend.example.com/sign-up?token=token-one",
        "https://frontend.example.com/sign-up?token=token-two",
    ]
    assert session.add.call_count == 2
    session.commit.assert_awaited_once()

    first_row = session.add.call_args_list[0].args[0]
    second_row = session.add.call_args_list[1].args[0]
    assert isinstance(first_row, InvitationToken)
    assert isinstance(second_row, InvitationToken)
    assert first_row.token == "token-one"
    assert second_row.token == "token-two"
    assert first_row.email == "founder@example.com"
    assert first_row.created_at == fixed_now
    assert first_row.expires_at == fixed_now + timedelta(days=3)
