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

from bot.app.web.admin_api_impl import broadcast as broadcast_module
from bot.app.web.admin_api_impl import common as common_module
from bot.app.web.admin_api_impl import users as users_module


class FakeResult:
    def __init__(self, rows=None):
        self._rows = rows or []

    def all(self):
        return self._rows

    def scalars(self):
        return self


class FakeSession:
    def __init__(self):
        self.execute = AsyncMock(side_effect=[FakeResult([]), FakeResult([]), FakeResult([])])
        self.committed = False

    async def __aenter__(self):
        return self

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

    async def commit(self):
        self.committed = True

    async def rollback(self):
        pass


def _active_subscription(panel_user_uuid="panel-from-sub"):
    return SimpleNamespace(
        subscription_id=10,
        panel_user_uuid=panel_user_uuid,
        panel_subscription_uuid=None,
        start_date=datetime(2026, 6, 1, tzinfo=timezone.utc),
        end_date=datetime(2026, 7, 1, tzinfo=timezone.utc),
        duration_months=1,
        is_active=True,
        status_from_panel="ACTIVE",
        traffic_limit_bytes=100,
        traffic_used_bytes=0,
        tier_baseline_bytes=0,
        topup_balance_bytes=0,
        premium_used_bytes=0,
        premium_baseline_bytes=0,
        premium_topup_balance_bytes=0,
        premium_topup_used_bytes=0,
        premium_bonus_bytes=0,
        regular_bonus_bytes=0,
        regular_unlimited_override=False,
        premium_unlimited_override=False,
        premium_is_limited=False,
        tariff_key="standard",
        auto_renew_enabled=True,
        provider="yookassa",
        is_throttled=False,
    )


class AdminPanelActivityTests(unittest.IsolatedAsyncioTestCase):
    def test_panel_activity_detects_connected_and_never_connected_users(self):
        self.assertEqual(
            common_module._panel_user_connection_activity(
                {"userTraffic": {"onlineAt": "2026-06-05T12:00:00Z"}}
            ),
            {
                "status": "connected",
                "last_connected_at": "2026-06-05T12:00:00+00:00",
            },
        )
        self.assertEqual(
            common_module._panel_user_connection_activity(
                {
                    "userTraffic": {
                        "onlineAt": None,
                        "firstConnectedAt": None,
                        "lastConnectedNodeUuid": None,
                        "lifetimeUsedTrafficBytes": 0,
                    },
                }
            ),
            {"status": "never", "last_connected_at": None},
        )
        self.assertEqual(
            common_module._panel_user_connection_activity(
                {"userTraffic": {"lifetimeUsedTrafficBytes": 1024}}
            ),
            {"status": "connected", "last_connected_at": None},
        )

    async def test_active_never_connected_audience_uses_panel_status(self):
        session = SimpleNamespace(
            execute=AsyncMock(
                return_value=FakeResult(
                    [
                        (1, "never-panel"),
                        (2, "connected-panel"),
                        (3, "missing-panel"),
                        (4, "also-never-panel"),
                        (4, "also-connected-panel"),
                    ]
                )
            )
        )

        async def get_user_by_uuid(panel_uuid):
            return {
                "never-panel": {
                    "userTraffic": {
                        "onlineAt": None,
                        "firstConnectedAt": None,
                        "lastConnectedNodeUuid": None,
                    },
                },
                "connected-panel": {"userTraffic": {"onlineAt": "2026-06-05T12:00:00Z"}},
                "also-never-panel": {
                    "userTraffic": {
                        "onlineAt": None,
                        "firstConnectedAt": None,
                        "lastConnectedNodeUuid": None,
                    },
                },
                "also-connected-panel": {
                    "userTraffic": {"lifetimeUsedTrafficBytes": 1},
                },
            }.get(panel_uuid)

        panel_service = SimpleNamespace(get_user_by_uuid=AsyncMock(side_effect=get_user_by_uuid))

        result = await broadcast_module._user_ids_with_active_subscription_never_connected(
            session,
            panel_service,
        )

        self.assertEqual(result, [1])
        self.assertEqual(
            [call.args[0] for call in panel_service.get_user_by_uuid.await_args_list],
            [
                "never-panel",
                "connected-panel",
                "missing-panel",
                "also-never-panel",
                "also-connected-panel",
            ],
        )

    async def test_user_detail_includes_last_vpn_connection_from_panel(self):
        session = FakeSession()
        user = SimpleNamespace(
            user_id=42,
            telegram_id=42,
            telegram_photo_url=None,
            username="alice",
            first_name="Alice",
            last_name=None,
            email=None,
            language_code="ru",
            is_banned=False,
            registration_date=datetime(2026, 6, 1, tzinfo=timezone.utc),
            panel_user_uuid=None,
            referral_code=None,
            referred_by_id=None,
            trial_eligibility_reset_at=None,
        )
        active_sub = _active_subscription("panel-from-sub")
        panel_service = SimpleNamespace(
            get_user_by_uuid=AsyncMock(
                return_value={
                    "subscriptionUrl": "https://panel.example/sub/short",
                    "userTraffic": {"onlineAt": "2026-06-05T12:00:00Z"},
                }
            )
        )
        request = SimpleNamespace(
            app={
                "settings": SimpleNamespace(SUBSCRIPTION_MINI_APP_URL=None),
                "async_session_factory": lambda: session,
                "subscription_service": SimpleNamespace(panel_service=panel_service),
            },
            match_info={"user_id": "42"},
        )

        with (
            patch.object(users_module, "_require_admin_user_id", return_value=100),
            patch.object(users_module.user_dal, "get_user_by_id", AsyncMock(return_value=user)),
            patch.object(
                users_module.subscription_dal,
                "get_active_subscription_by_user_id",
                AsyncMock(return_value=active_sub),
            ),
            patch.object(
                users_module.payment_dal,
                "get_user_total_paid",
                AsyncMock(return_value=0),
            ),
            patch.object(
                users_module.message_log_dal,
                "count_user_message_logs",
                AsyncMock(return_value=0),
            ),
            patch.object(
                users_module.user_dal,
                "get_referrer_for_user",
                AsyncMock(return_value=None),
            ),
            patch.object(
                users_module.user_dal,
                "count_users_referred_by",
                AsyncMock(return_value=0),
            ),
            patch.object(users_module, "_bulk_user_avatar_keys", AsyncMock(return_value={})),
            patch.object(
                users_module.user_dal,
                "ensure_referral_code",
                AsyncMock(return_value="REF"),
            ),
        ):
            response = await users_module.admin_user_detail_route(request)

        payload = json.loads(response.text)
        self.assertEqual(response.status, 200)
        self.assertEqual(payload["subscription_url"], "https://panel.example/sub/short")
        self.assertEqual(payload["vpn_connection_status"], "connected")
        self.assertEqual(payload["last_vpn_connected_at"], "2026-06-05T12:00:00+00:00")
        panel_service.get_user_by_uuid.assert_awaited_once_with("panel-from-sub")


if __name__ == "__main__":
    unittest.main()
