"""Premium experience integration tests (Phase 10).

UX integration only — surfacing existing Premium features. No Premium business
rule is exercised here beyond the existing services.
"""

import os
import unittest
from datetime import timedelta
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.marketplace.services import (
    get_featurable_listing_cards,
    is_listing_currently_featured,
)
from app.models import (
    Category,
    Listing,
    ListingCondition,
    ListingStatus,
    ListingVisibility,
    ReviewStatus,
    Subscription,
    User,
)
from app.models.base import utcnow
from app.models.enums import UserRole
from app.premium.services import activate_premium


class _QC:
    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 PremiumIntegrationTests(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()

    def _user(self, email, premium=False):
        u = User(is_verified=True, email=email, password_hash=generate_password_hash("Passw0rd!"), role=UserRole.STUDENT)
        db.session.add(u)
        db.session.commit()
        if premium:
            activate_premium(u)
        return u

    def _category(self):
        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()
        return cat

    def _listing(self, seller, idx=1, status=ListingStatus.AVAILABLE, deleted=False, featured=False):
        l = Listing(
            sku=f"SKU-{seller.id}-{idx}", seller_id=seller.id, category_id=self._category().id,
            title=f"Item {idx}", slug=f"item-{seller.id}-{idx}", description="d", price=Decimal("50.00"),
            condition=ListingCondition.GOOD, status=status, visibility=ListingVisibility.PUBLIC,
            review_status=ReviewStatus.APPROVED,
        )
        if featured:
            l.is_featured = True
            l.featured_at = utcnow()
            l.featured_until = utcnow() + timedelta(days=7)
        if deleted:
            l.deleted_at = utcnow()
            l.status = ListingStatus.REMOVED
        db.session.add(l)
        db.session.commit()
        return l

    def _client(self, user):
        c = self.app.test_client()
        c.post("/login", data={"email": user.email, "password": "Passw0rd!"})
        return c

    # --- dashboard rendering / pricing hidden ---
    def test_dashboard_rendering_for_premium(self):
        u = self._user("p@c.edu", premium=True)
        html = self._client(u).get("/app/premium/").get_data(as_text=True)
        self.assertIn("Premium Dashboard", html)
        self.assertIn("Premium Active", html)
        self.assertIn("Analytics Preview", html)
        self.assertIn("Your Premium Benefits", html)
        self.assertIn("What's Next", html)  # replaces the old Quick Actions row
        self.assertNotIn("Quick Actions", html)

    def test_pricing_hidden_for_premium(self):
        u = self._user("p@c.edu", premium=True)
        html = self._client(u).get("/app/premium/").get_data(as_text=True)
        self.assertNotIn("Simple, student-friendly pricing", html)   # pricing
        self.assertNotIn("How Premium works", html)
        self.assertNotIn("premium-roadmap", html)

    def test_pricing_shown_for_free_user(self):
        u = self._user("f@c.edu")
        html = self._client(u).get("/app/premium/").get_data(as_text=True)
        self.assertIn("Simple, student-friendly pricing", html)
        self.assertNotIn("Premium Dashboard", html)

    # --- featured card / no-featured state ---
    def test_no_featured_state(self):
        u = self._user("p@c.edu", premium=True)
        self._listing(u, 1)
        html = self._client(u).get("/app/premium/").get_data(as_text=True)
        self.assertIn("No listing is currently featured", html)
        self.assertIn("Choose Featured Listing", html)

    def test_featured_card(self):
        u = self._user("p@c.edu", premium=True)
        self._listing(u, 1, featured=True)
        html = self._client(u).get("/app/premium/").get_data(as_text=True)
        self.assertIn("Change Featured Listing", html)
        self.assertIn("Featured until", html)

    # --- choose featured page ---
    def test_choose_featured_lists_only_eligible(self):
        u = self._user("p@c.edu", premium=True)
        avail = self._listing(u, 1)
        self._listing(u, 2, status=ListingStatus.SOLD)
        self._listing(u, 3, deleted=True)
        self._listing(u, 4, featured=True)
        cards = get_featurable_listing_cards(u)
        titles = {c["title"] for c in cards}
        self.assertEqual(titles, {"Item 1"})   # only the available, non-featured one
        self.assertEqual(cards[0]["public_id"], avail.public_id)

    def test_choose_featured_page_renders(self):
        u = self._user("p@c.edu", premium=True)
        self._listing(u, 1)
        html = self._client(u).get("/app/premium/feature-listing").get_data(as_text=True)
        self.assertIn("Feature This Listing", html)
        self.assertIn("Choose a Featured Listing", html)

    def test_choose_featured_denied_for_free(self):
        u = self._user("f@c.edu")
        resp = self._client(u).get("/app/premium/feature-listing")
        self.assertEqual(resp.status_code, 302)  # redirected to premium index

    # --- feature action ---
    def test_feature_action_features_listing(self):
        u = self._user("p@c.edu", premium=True)
        listing = self._listing(u, 1)
        self._client(u).post(f"/app/premium/feature-listing/{listing.public_id}")
        db.session.expire_all()
        self.assertTrue(is_listing_currently_featured(db.session.get(Listing, listing.id)))

    def test_feature_action_replaces_previous(self):
        u = self._user("p@c.edu", premium=True)
        first = self._listing(u, 1, featured=True)
        second = self._listing(u, 2)
        self._client(u).post(f"/app/premium/feature-listing/{second.public_id}")
        db.session.expire_all()
        self.assertTrue(is_listing_currently_featured(db.session.get(Listing, second.id)))
        self.assertFalse(is_listing_currently_featured(db.session.get(Listing, first.id)))

    # --- my listings integration ---
    def test_my_listings_shows_feature_for_premium(self):
        u = self._user("p@c.edu", premium=True)
        self._listing(u, 1)
        html = self._client(u).get("/app/my-listings").get_data(as_text=True)
        self.assertIn("Feature Listing", html)

    def test_my_listings_shows_upgrade_for_free(self):
        u = self._user("f@c.edu")
        self._listing(u, 1)
        html = self._client(u).get("/app/my-listings").get_data(as_text=True)
        # Free owners still see Feature Listing / View Analytics in the menu, but
        # premium-gated (a lock icon linking to the upgrade page).
        self.assertIn("Feature Listing", html)
        self.assertIn("bi-lock-fill", html)

    # --- listing detail integration ---
    def test_listing_detail_premium_tools_for_premium_owner(self):
        u = self._user("p@c.edu", premium=True)
        listing = self._listing(u, 1)
        html = self._client(u).get(f"/app/listings/{listing.public_id}").get_data(as_text=True)
        self.assertIn("Listing Tools", html)
        self.assertIn("Feature Listing", html)
        self.assertIn("View Analytics", html)

    def test_listing_detail_premium_tools_upsell_for_free_owner(self):
        u = self._user("f@c.edu")
        listing = self._listing(u, 1)
        html = self._client(u).get(f"/app/listings/{listing.public_id}").get_data(as_text=True)
        self.assertIn("Listing Tools", html)
        self.assertIn("Upgrade to Premium", html)

    # --- analytics preview ---
    def test_analytics_preview(self):
        u = self._user("p@c.edu", premium=True)
        self._listing(u, 1)
        html = self._client(u).get("/app/premium/").get_data(as_text=True)
        self.assertIn("Open Full Analytics", html)
        self.assertIn("Views this month", html)

    # --- expiry reminder ---
    def test_expiry_reminder_within_7_days(self):
        u = self._user("p@c.edu", premium=True)
        sub = db.session.scalar(db.select(Subscription).where(Subscription.user_id == u.id))
        sub.end_date = utcnow() + timedelta(days=3)
        db.session.commit()
        html = self._client(u).get("/app/premium/").get_data(as_text=True)
        self.assertIn("Premium expires in", html)
        self.assertIn("Renew", html)

    def test_no_expiry_reminder_when_plenty_of_time(self):
        u = self._user("p@c.edu", premium=True)  # default 30-day window
        html = self._client(u).get("/app/premium/").get_data(as_text=True)
        self.assertNotIn("Premium expires in", html)

    # --- marketplace nudge ---
    def test_marketplace_nudge_when_no_featured(self):
        u = self._user("p@c.edu", premium=True)
        self._listing(u, 1)
        html = self._client(u).get("/marketplace").get_data(as_text=True)
        self.assertIn("mkPremiumNudge", html)
        self.assertIn("Increase visibility by featuring", html)

    def test_no_marketplace_nudge_when_featured(self):
        u = self._user("p@c.edu", premium=True)
        self._listing(u, 1, featured=True)
        html = self._client(u).get("/marketplace").get_data(as_text=True)
        self.assertNotIn("mkPremiumNudge", html)

    # --- performance: choose-featured has no N+1 ---
    def test_featurable_cards_no_n_plus_1(self):
        u = self._user("p@c.edu", premium=True)
        for i in range(4):
            self._listing(u, i)
        with _QC() as c1:
            get_featurable_listing_cards(u)
        small = c1.n
        for i in range(4, 40):
            self._listing(u, i)
        with _QC() as c2:
            get_featurable_listing_cards(u)
        self.assertEqual(small, c2.n)  # constant regardless of listing count


if __name__ == "__main__":
    unittest.main()
