"""Reputation & trust dashboard tests (Trust System — Phase 3)."""

import os
import unittest
from decimal import Decimal

os.environ["TEST_DATABASE_URL"] = "sqlite:///:memory:"

from flask import render_template_string
from werkzeug.security import generate_password_hash

from app import create_app
from app.extensions import db
from app.models import (
    Category,
    Conversation,
    Listing,
    ListingCondition,
    ListingStatus,
    ListingVisibility,
    Offer,
    OfferStatus,
    ReviewStatus,
    User,
    UserReputation,
)
from app.marketplace.services import complete_transaction
from app.negotiation.services import accept_offer
from app.reviews.reputation import (
    get_recent_reviews_for_user,
    get_reputation,
)
from app.reviews.services import submit_review


class ReputationTests(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 _listing(self, seller, idx=1):
        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,
        )
        db.session.add(listing)
        db.session.commit()
        return listing

    def _complete(self, seller, buyer, idx):
        listing = self._listing(seller, idx)
        convo = Conversation(listing_id=listing.id, buyer_id=buyer.id, seller_id=seller.id)
        db.session.add(convo)
        db.session.commit()
        offer = Offer(listing_id=listing.id, conversation_id=convo.id, buyer_id=buyer.id,
                      seller_id=seller.id, amount=Decimal("40.00"), status=OfferStatus.PENDING)
        db.session.add(offer)
        db.session.commit()
        accept_offer(offer, seller)
        return complete_transaction(listing, seller)

    def _sold_and_reviewed(self, seller, buyer_email, rating, idx):
        buyer = self._user(buyer_email)
        tx = self._complete(seller, buyer, idx)
        submit_review(tx, buyer, rating)
        return tx

    # --- automatic creation + review aggregates ---
    def test_reputation_created_automatically_on_transaction(self):
        seller, buyer = self._user("s@c.edu"), self._user("b@c.edu")
        self.assertIsNone(get_reputation(seller.id))  # none before
        self._complete(seller, buyer, 1)
        self.assertIsNotNone(get_reputation(seller.id))  # created on completion

    def test_review_updates_average_rating(self):
        seller = self._user("s@c.edu")
        self._sold_and_reviewed(seller, "b1@c.edu", 4, 1)
        self._sold_and_reviewed(seller, "b2@c.edu", 2, 2)
        rep = get_reputation(seller.id)
        self.assertEqual(rep.rating, 3.0)  # (4 + 2) / 2

    def test_review_count_increments(self):
        seller = self._user("s@c.edu")
        self._sold_and_reviewed(seller, "b1@c.edu", 5, 1)
        self.assertEqual(get_reputation(seller.id).review_count, 1)
        self._sold_and_reviewed(seller, "b2@c.edu", 5, 2)
        self.assertEqual(get_reputation(seller.id).review_count, 2)

    def test_positive_percentage(self):
        seller = self._user("s@c.edu")
        # ratings 5, 4, 2 → two "positive" (>=4) of three = 66.67%
        self._sold_and_reviewed(seller, "b1@c.edu", 5, 1)
        self._sold_and_reviewed(seller, "b2@c.edu", 4, 2)
        self._sold_and_reviewed(seller, "b3@c.edu", 2, 3)
        rep = get_reputation(seller.id)
        self.assertEqual(rep.review_count, 3)
        self.assertAlmostEqual(float(rep.positive_review_percentage), 66.67, places=2)

    # --- transaction aggregates ---
    def test_transaction_updates_completed_sales(self):
        seller, b1, b2 = self._user("s@c.edu"), self._user("b1@c.edu"), self._user("b2@c.edu")
        self._complete(seller, b1, 1)
        self._complete(seller, b2, 2)
        self.assertEqual(get_reputation(seller.id).completed_sales, 2)

    def test_transaction_updates_completed_purchases(self):
        s1, s2, buyer = self._user("s1@c.edu"), self._user("s2@c.edu"), self._user("b@c.edu")
        self._complete(s1, buyer, 1)
        self._complete(s2, buyer, 1)
        self.assertEqual(get_reputation(buyer.id).completed_purchases, 2)

    # --- recent reviews ---
    def test_recent_reviews_limited_to_five(self):
        seller = self._user("s@c.edu")
        for i in range(6):
            self._sold_and_reviewed(seller, f"b{i}@c.edu", 5, i + 1)
        recent = get_recent_reviews_for_user(seller, limit=5)
        self.assertEqual(len(recent), 5)
        self.assertIn("reviewer_first_name", recent[0])

    # --- listing card rendering (cached values only) ---
    def _render_card(self, listing, reputations):
        with self.app.test_request_context():
            return render_template_string(
                "{% from 'components/listing_card.html' import listing_card with context %}"
                "{{ listing_card(listing, reputations=reps) }}",
                listing=listing, reps=reputations,
            )

    def test_listing_card_displays_reputation(self):
        seller = self._user("s@c.edu")
        self._sold_and_reviewed(seller, "b@c.edu", 5, 1)
        listing = self._listing(seller, 2)  # another available listing by the same seller
        html = self._render_card(listing, {seller.id: get_reputation(seller.id)})
        self.assertIn("rep-inline", html)
        self.assertIn("5.0", html)
        self.assertIn("(1)", html)

    def test_new_seller_label(self):
        seller = self._user("s@c.edu")
        listing = self._listing(seller, 1)
        html = self._render_card(listing, {})  # no reputation row
        self.assertIn("New Seller", html)
        self.assertNotIn("rep-inline", html)

    # --- public seller profile ---
    def test_public_profile_renders_reputation_and_reviews(self):
        seller = self._user("s@c.edu")
        self._sold_and_reviewed(seller, "b1@c.edu", 5, 1)
        self._sold_and_reviewed(seller, "b2@c.edu", 4, 2)
        client = self.app.test_client()
        resp = client.get(f"/users/{seller.public_id}")
        self.assertEqual(resp.status_code, 200)
        html = resp.get_data(as_text=True)
        self.assertIn("Seller reputation", html)
        self.assertIn("Recent reviews", html)
        self.assertIn("4.5", html)  # (5 + 4) / 2
        self.assertIn("2 reviews", html)

    def test_public_profile_new_seller(self):
        seller = self._user("s@c.edu")
        self._listing(seller, 1)
        client = self.app.test_client()
        html = client.get(f"/users/{seller.public_id}").get_data(as_text=True)
        self.assertIn("New Seller", html)
        self.assertIn("No reviews yet.", html)

    def test_seller_url_alias_resolves(self):
        seller = self._user("s@c.edu")
        client = self.app.test_client()
        self.assertEqual(client.get(f"/seller/{seller.public_id}").status_code, 200)


if __name__ == "__main__":
    unittest.main()
