"""
The "invite user" email's link (send_invitation_email, arq_app.py) must
resolve to the invited tenant's OWN admin subdomain — ADMIN_PORTAL_URL has
no {subdomain} token and can never do that, so it must not be used to build
the link.

The job opens its OWN session via arq_app.AsyncSessionLocal and commits, so
this monkeypatches that sessionmaker to point at the test engine (same
pattern as test_scheduling_notification_jobs.py) rather than the db_session
fixture, and stubs the external email dispatcher.
"""
from unittest.mock import AsyncMock, patch
from uuid import uuid4

import pytest
import pytest_asyncio
from sqlalchemy import delete
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

import src.core.email_dispatch as email_dispatch_mod
import src.worker.arq_app as arq_app
from src.apps.auth.models.user import User
from src.apps.tenants.models.account import Account
from src.core.config import settings
from src.core.security import hash_password
from src.worker.arq_app import send_invitation_email

_TEST_DATABASE_URL = settings.DATABASE_URL.replace("/indelis", "/indelis_test")
if not _TEST_DATABASE_URL.startswith("postgresql+psycopg://"):
    _TEST_DATABASE_URL = "postgresql+psycopg://" + _TEST_DATABASE_URL.split("://", 1)[-1]

_engine = create_async_engine(_TEST_DATABASE_URL, echo=False)
_Session = async_sessionmaker(_engine, class_=AsyncSession, expire_on_commit=False)


@pytest_asyncio.fixture
async def invited_user():
    async with _Session() as s:
        account = Account(
            organization_name="Invite Test Cemetery",
            subdomain=f"invite-{uuid4().hex[:10]}",
            contact_email="office@invite-test.example.com",
            plan="starter",
            status="active",
        )
        s.add(account)
        await s.flush()

        user = User(
            tenant_id=account.id,
            email=f"invitee-{uuid4().hex[:8]}@example.com",
            password_hash=hash_password("placeholder-not-usable"),
            first_name="Invited",
            last_name="Person",
            role="staff",
            status="invited",
        )
        s.add(user)
        await s.flush()
        await s.commit()
        yield {"tenant": account, "user": user}

    async with _Session() as s:
        await s.execute(delete(User).where(User.tenant_id == account.id))
        await s.execute(delete(Account).where(Account.id == account.id))
        await s.commit()


@pytest.fixture
def patch_job(monkeypatch):
    monkeypatch.setattr(arq_app, "AsyncSessionLocal", _Session)
    send_mock = AsyncMock(return_value=__import__("types").SimpleNamespace(sent=True, skipped_reason=None))
    monkeypatch.setattr(email_dispatch_mod.email_dispatch_service, "send", send_mock)
    return send_mock


@pytest.mark.asyncio
async def test_invite_url_uses_tenant_subdomain_template(invited_user, patch_job):
    tenant = invited_user["tenant"]
    user = invited_user["user"]

    with patch.object(settings, "ADMIN_PORTAL_URL_TEMPLATE", "https://{subdomain}-admin-dev.dreamztesting.com"):
        await send_invitation_email({}, str(user.id), str(tenant.id))

    invite_url = patch_job.call_args.kwargs["context"]["invite_url"]
    assert invite_url.startswith(f"https://{tenant.subdomain}-admin-dev.dreamztesting.com/accept-invitation?token=")


@pytest.mark.asyncio
async def test_invite_url_falls_back_to_subdomain_when_template_unset(invited_user, patch_job):
    tenant = invited_user["tenant"]
    user = invited_user["user"]

    with patch.object(settings, "ADMIN_PORTAL_URL_TEMPLATE", ""):
        await send_invitation_email({}, str(user.id), str(tenant.id))

    invite_url = patch_job.call_args.kwargs["context"]["invite_url"]
    # Even the bare fallback must carry the tenant's own subdomain — never a
    # subdomain-less host like the old hardcoded http://localhost:3000.
    assert invite_url.startswith(f"https://{tenant.subdomain}.{settings.APP_DOMAIN}/accept-invitation?token=")
