import unittest

from app_master_service import (
    APP_LABELS,
    EVENT_SOURCES,
    app_stat_categories,
    format_app_event_message,
    format_app_stats_message,
    list_apps,
    resolve_app_key,
)


class AppMasterServiceFormattingTests(unittest.TestCase):
    def test_lists_every_managed_app_and_resolves_labels(self):
        apps = list_apps()

        self.assertEqual(len(apps), len(APP_LABELS))
        self.assertEqual(resolve_app_key("가세로캠"), "onetakeduo")
        self.assertEqual(resolve_app_key("wallet keeper"), "wallet_keeper")
        self.assertEqual(resolve_app_key("StoneMaster"), "stonemaster")
        self.assertIsNone(resolve_app_key("없는 앱"))

    def test_exposes_only_supported_stat_categories(self):
        self.assertEqual(
            [category for category, _ in app_stat_categories("babynote")],
            ["overview", "users", "activity", "purchases", "inquiries"],
        )
        self.assertEqual(
            [category for category, _ in app_stat_categories("qnote")],
            ["overview", "activity"],
        )

    def test_formats_inquiry_notification_with_admin_link(self):
        message = format_app_event_message(
            {
                "event_type": "inquiry",
                "app_key": "wallet_keeper",
                "title": "자산이 안 보여요",
                "detail": "클라우드 저장 후 목록이 비어 있습니다.",
                "actor": "홍길동",
            }
        )

        self.assertIn("[새 문의]", message)
        self.assertIn("앱: 지갑지켜", message)
        self.assertIn("자산이 안 보여요", message)
        self.assertIn("/app/wallet_keeper/inquiries", message)

    def test_formats_purchase_notification_with_currency(self):
        message = format_app_event_message(
            {
                "event_type": "purchase",
                "app_key": "onetakeduo",
                "title": "com.brosister.onetakeduo.pro",
                "amount": 14900,
                "currency": "KRW",
                "platform": "android",
                "actor": "user-1",
            }
        )

        self.assertIn("[결제 완료]", message)
        self.assertIn("앱: 가세로캠", message)
        self.assertIn("금액: ₩14,900", message)
        self.assertIn("플랫폼: ANDROID", message)

    def test_formats_domain_assistance_notification_without_credentials(self):
        message = format_app_event_message(
            {
                "event_type": "domain_assistance",
                "event_id": 17,
                "app_key": "official-mail",
                "title": "example.com",
                "detail": "가비아",
                "actor": "owner@example.com",
                "notification_email": "notify@example.com",
                "amount": 10_000,
                "currency": "KRW",
            }
        )

        self.assertIn("[도메인 연결 설정 대행 신청]", message)
        self.assertIn("신청 번호: #17", message)
        self.assertIn("도메인: example.com", message)
        self.assertIn("완료 후 결제 예정: ₩10,000", message)
        self.assertIn("view=domain-assistance", message)
        self.assertNotIn("password", message.lower())

    def test_formats_failed_polar_payment_with_decline_detail(self):
        message = format_app_event_message(
            {
                "event_type": "polar_payment",
                "app_key": "official-mail",
                "title": "성장 플랜",
                "actor": "owner@example.com",
                "amount": 5.99,
                "currency": "USD",
                "status": "failed",
                "payment_trigger": "purchase",
                "card_brand": "visa",
                "card_last_four": "1305",
                "detail": "Your card was declined.",
                "decline_reason": "card_declined",
                "polar_payment_id": "payment-id",
            }
        )

        self.assertIn("[해외 결제 실패]", message)
        self.assertIn("금액: USD 5.99", message)
        self.assertIn("카드: VISA •••• 1305", message)
        self.assertIn("Your card was declined.", message)
        self.assertIn("view=charges", message)

    def test_formats_domestic_payment_method_registration(self):
        message = format_app_event_message(
            {
                "event_type": "payment_method_registered",
                "region": "domestic",
                "actor": "owner@example.com",
                "card_brand": "국민",
                "card_last_four": "1305",
            }
        )

        self.assertIn("[국내 결제수단 등록 완료]", message)
        self.assertIn("국민 •••• 1305", message)
        self.assertIn("Toss Pay", message)
        self.assertIn("view=charges", message)

    def test_formats_polar_payment_method_and_subscription_activation(self):
        message = format_app_event_message(
            {
                "event_type": "payment_method_registered",
                "region": "overseas",
                "actor": "owner@example.com",
                "card_brand": "visa",
                "card_last_four": "1305",
            }
        )

        self.assertIn("[해외 결제수단 등록 및 정기결제 활성화]", message)
        self.assertIn("VISA •••• 1305", message)
        self.assertIn("Polar", message)

    def test_formats_initial_toss_charge_as_subscription_activation(self):
        message = format_app_event_message(
            {
                "event_type": "toss_payment",
                "actor": "owner@example.com",
                "amount": 4290,
                "currency": "KRW",
                "is_initial_subscription_charge": 1,
                "payment_id": "om-manual-1",
                "payment_trigger": "manual",
                "status": "success",
                "title": "성장플랜 1명",
            }
        )

        self.assertIn("[국내 정기결제 활성화]", message)
        self.assertIn("₩4,290", message)
        self.assertIn("om-manual-1", message)

    def test_formats_failed_toss_cycle_charge(self):
        message = format_app_event_message(
            {
                "event_type": "toss_payment",
                "actor": "owner@example.com",
                "amount": 4290,
                "currency": "KRW",
                "detail": "카드 승인이 거절되었습니다.",
                "decline_reason": "CARD_DECLINED",
                "payment_id": "om-cycle-1",
                "payment_trigger": "cycle",
                "status": "failed",
                "title": "성장플랜 1명",
            }
        )

        self.assertIn("[국내 정기결제 실패]", message)
        self.assertIn("카드 승인이 거절되었습니다.", message)
        self.assertIn("CARD_DECLINED", message)

    def test_formats_overview_without_fake_user_zeroes(self):
        message = format_app_stats_message(
            "tapcounter",
            "overview",
            {
                "users": {"tracked": False},
                "metrics": [
                    {"label": "광고 운영 모드", "value": "release", "format": "text"},
                    {"label": "활성 광고 제거", "value": 2, "format": "number"},
                ],
                "purchases": {"supported": True, "paid": 3},
                "inquiries": {"supported": False},
            },
        )

        self.assertIn("사용자: 로그인 사용자 추적 안 함", message)
        self.assertIn("광고 운영 모드: 릴리즈", message)
        self.assertIn("결제 완료: 3건", message)
        self.assertNotIn("전체 사용자: 0", message)

    def test_purchase_events_use_completion_times_and_exclude_restores(self):
        purchase_sql = "\n".join(
            source["sql"]
            for source in EVENT_SOURCES
            if source["event_type"] == "purchase"
        ).lower()

        self.assertNotIn("status in ('completed', 'restored')", purchase_sql)
        self.assertNotIn("coalesce(updated_at", purchase_sql)
        self.assertIn("approved_at", purchase_sql)
        self.assertIn("verified_at", purchase_sql)

    def test_balancepick_purchase_event_is_verified_non_test_and_idempotent(self):
        balancepick_sql = next(
            source["sql"].lower()
            for source in EVENT_SOURCES
            if source["source"] == "balancepick_purchases"
        )

        self.assertIn("select id as event_id", balancepick_sql)
        self.assertIn("from balancepick_purchases", balancepick_sql)
        self.assertIn("verified_at is not null", balancepick_sql)
        self.assertIn("purchase_state = 0", balancepick_sql)
        self.assertIn("is_test_purchase = 0", balancepick_sql)
        self.assertIn("purchased_at as event_time", balancepick_sql)
        self.assertNotIn("unix_timestamp(started_at)", balancepick_sql)


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