import json
from datetime import datetime, timezone
from types import SimpleNamespace
from unittest import IsolatedAsyncioTestCase
from unittest.mock import AsyncMock, patch

import bot.app.web.subscription_webapp  # noqa: F401
from bot.app.web.webapp import billing as billing_module


class _Session:
    def __init__(self):
        self.commit_count = 0
        self.rollback_count = 0

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, tb):
        return False

    async def commit(self):
        self.commit_count += 1

    async def rollback(self):
        self.rollback_count += 1


class _SessionFactory:
    def __init__(self, session):
        self.session = session

    def __call__(self):
        return self.session


class WebAppTrialActivationTests(IsolatedAsyncioTestCase):
    async def test_email_only_trial_activation_is_written_to_admin_logs(self):
        session = _Session()
        end_date = datetime(2026, 1, 9, 3, 4, tzinfo=timezone.utc)
        settings = SimpleNamespace(
            TRIAL_ENABLED=True,
            TRIAL_DURATION_DAYS=7,
            TRIAL_TRAFFIC_LIMIT_GB=10,
            LOG_TRIAL_ACTIVATIONS=False,
        )
        db_user = SimpleNamespace(
            user_id=42,
            is_banned=False,
            username=None,
            first_name=None,
            email="email-only@example.com",
        )
        subscription_service = SimpleNamespace(
            activate_trial_subscription=AsyncMock(
                return_value={
                    "activated": True,
                    "days": 7,
                    "end_date": end_date,
                    "traffic_gb": 10,
                    "subscription_url": "https://panel.example/sub",
                }
            )
        )
        request = SimpleNamespace(
            app={
                "settings": settings,
                "async_session_factory": _SessionFactory(session),
                "subscription_service": subscription_service,
            }
        )

        with (
            patch.object(billing_module, "_require_user_id", return_value=42),
            patch.object(
                billing_module,
                "_enforce_webapp_rate_limit",
                AsyncMock(return_value=None),
            ),
            patch.object(
                billing_module.user_dal,
                "get_user_by_id",
                AsyncMock(return_value=db_user),
            ),
            patch.object(
                billing_module,
                "prepare_config_links",
                AsyncMock(return_value=("https://panel.example/sub", "https://connect.example")),
            ),
            patch.object(
                billing_module.message_log_dal,
                "create_message_log_no_commit",
                AsyncMock(),
            ) as create_log,
            patch.object(
                billing_module,
                "invalidate_webapp_user_caches",
                AsyncMock(),
            ),
            patch("db.dal.ad_dal.mark_trial_activated", AsyncMock()) as mark_trial_activated,
        ):
            response = await billing_module.activate_trial_route(request)

        payload = json.loads(response.text)
        self.assertEqual(response.status, 200)
        self.assertTrue(payload["activated"])
        subscription_service.activate_trial_subscription.assert_awaited_once_with(session, 42)
        create_log.assert_awaited_once()
        log_payload = create_log.await_args.args[1]
        self.assertEqual(log_payload["user_id"], 42)
        self.assertEqual(log_payload["target_user_id"], 42)
        self.assertEqual(log_payload["event_type"], "webapp_trial_activate")
        self.assertFalse(log_payload["is_admin_event"])
        self.assertIn("email-only@example.com", log_payload["content"])
        mark_trial_activated.assert_awaited_once_with(session, 42)
        self.assertEqual(session.commit_count, 2)
        self.assertEqual(session.rollback_count, 0)
