"""Automatic trust badge tests (Trust System — Phase 4)."""

import os
import unittest
from decimal import Decimal

os.environ["TEST_DATABASE_URL"] = "sqlite:///:memory:"

from sqlalchemy import event
from werkzeug.security import generate_password_hash

from app import create_app
from app.extensions import db
from app.models import (
    Category,
    Listing,
    ListingCondition,
    ListingStatus,
    ListingVisibility,
    ReviewStatus,
    User,
    UserReputation,
)
from app.reviews.badges import get_badge_map, get_badges, has_badge


class _QC:
    """Count executed queries within a `with` block."""
    def __init__(self):
        self.n = 0

    def __enter__(self):
        self._e = db.engine
        event.listen(self._e, "after_cursor_execute", self._cb)
        return self

    def __exit__(self, *a):
        event.remove(self._e, "after_cursor_execute", self._cb)

    def _cb(self, *a):
        self.n += 1


class BadgeTests(unittest.TestCase):
    def setUp(self):
        self.app = create_app("testing")
        self.ctx = self.app.app_context()
        self.ctx.push()
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()
        self.ctx.pop()

    # --- factories ---
    def _user(self, email):
        u = User(is_verified=True, email=email, password_hash=generate_password_hash("Passw0rd!"))
        db.session.add(u)
        db.session.commit()
        return u

    def _rep(self, user, **kwargs):
        rep = UserReputation(user_id=user.id, **kwargs)
        db.session.add(rep)
        db.session.commit()
        return rep

    def _listing(self, seller, idx=1, favorite_count=0):
        cat = db.session.scalar(db.select(Category).filter_by(name="Books"))
        if cat is None:
            cat = Category(name="Books", description="d", is_active=True, display_order=1)
            db.session.add(cat)
            db.session.commit()
        listing = Listing(
            sku=f"SKU-{seller.id}-{idx}", seller_id=seller.id, category_id=cat.id,
            title=f"Item {idx}", slug=f"item-{seller.id}-{idx}", description="d", price=Decimal("50.00"),
            condition=ListingCondition.GOOD, status=ListingStatus.AVAILABLE, visibility=ListingVisibility.PUBLIC,
            review_status=ReviewStatus.APPROVED, favorite_count=favorite_count,
        )
        db.session.add(listing)
        db.session.commit()
        return listing

    def _keys(self, user):
        return [b["key"] for b in get_badges(user)]

    # --- individual badge rules ---
    def test_trusted_seller_awarded(self):
        u = self._user("s@c.edu")
        self._rep(u, average_rating=Decimal("4.6"), review_count=5, completed_sales=10)
        self.assertTrue(has_badge(u, "trusted_seller"))

    def test_trusted_seller_not_awarded_below_threshold(self):
        u = self._user("s@c.edu")
        self._rep(u, average_rating=Decimal("4.6"), review_count=5, completed_sales=9)  # 9 < 10
        self.assertFalse(has_badge(u, "trusted_seller"))

    def test_top_seller_awarded(self):
        u = self._user("s@c.edu")
        self._rep(u, average_rating=Decimal("4.9"), review_count=20, completed_sales=50)
        self.assertTrue(has_badge(u, "top_seller"))

    def test_great_communicator_awarded(self):
        u = self._user("s@c.edu")
        self._rep(u, positive_review_percentage=Decimal("96"), review_count=20, average_rating=Decimal("4.0"))
        self.assertTrue(has_badge(u, "great_communicator"))

    def test_quick_responder_with_response_data(self):
        u = self._user("s@c.edu")
        self._rep(u, average_response_minutes=20)
        self.assertTrue(has_badge(u, "quick_responder"))

    def test_quick_responder_false_when_data_unavailable(self):
        u = self._user("s@c.edu")
        self._rep(u, average_response_minutes=None)  # tracking not implemented yet
        self.assertFalse(has_badge(u, "quick_responder"))
        # also safe when the user has no reputation row at all
        self.assertFalse(has_badge(self._user("n@c.edu"), "quick_responder"))

    def test_popular_seller_uses_favourite_counts(self):
        u = self._user("s@c.edu")
        self._listing(u, 1, favorite_count=60)
        self._listing(u, 2, favorite_count=45)  # 105 total ≥ 100
        self.assertTrue(has_badge(u, "popular_seller"))
        # below threshold
        low = self._user("l@c.edu")
        self._listing(low, 1, favorite_count=99)
        self.assertFalse(has_badge(low, "popular_seller"))

    def test_new_seller_has_no_badges(self):
        self.assertEqual(get_badges(self._user("n@c.edu")), [])

    # --- card priority + batching ---
    def test_card_shows_highest_priority_badge(self):
        u = self._user("s@c.edu")
        # qualifies for Trusted (priority 2) AND Popular (priority 5)
        self._rep(u, average_rating=Decimal("4.6"), review_count=5, completed_sales=10)
        listing = self._listing(u, 1, favorite_count=150)
        badges = get_badge_map([listing])[u.id]
        self.assertEqual(badges[0]["key"], "trusted_seller")  # highest priority first
        self.assertIn("popular_seller", [b["key"] for b in badges])

    def test_badge_map_avoids_n_plus_1(self):
        # 3 sellers, each a listing + reputation
        for i in range(3):
            s = self._user(f"s{i}@c.edu")
            self._rep(s, average_rating=Decimal("4.6"), review_count=5, completed_sales=10)
            self._listing(s, 1, favorite_count=10)
        listings = db.session.scalars(db.select(Listing)).all()
        with _QC() as c1:
            get_badge_map(listings)
        base = c1.n
        for i in range(3, 12):  # many more sellers
            s = self._user(f"s{i}@c.edu")
            self._rep(s, average_rating=Decimal("4.6"), review_count=5, completed_sales=10)
            self._listing(s, 1, favorite_count=10)
        listings = db.session.scalars(db.select(Listing)).all()
        with _QC() as c2:
            get_badge_map(listings)
        self.assertEqual(base, c2.n)  # constant query count regardless of sellers

    # --- public profile display ---
    def test_public_profile_displays_badges(self):
        u = self._user("s@c.edu")
        self._rep(u, average_rating=Decimal("4.7"), review_count=8, completed_sales=12)
        html = self.app.test_client().get(f"/users/{u.public_id}").get_data(as_text=True)
        self.assertIn("Trusted Seller", html)
        self.assertIn("trust-badge", html)


if __name__ == "__main__":
    unittest.main()
