import os
import unittest
from decimal import Decimal

os.environ["TEST_DATABASE_URL"] = "sqlite:///:memory:"

from app import create_app
from app.extensions import db
from app.models import (
    Category,
    Conversation,
    Listing,
    ListingCondition,
    ListingStatus,
    ListingVisibility,
    Notification,
    Offer,
    OfferStatus,
    ReviewStatus,
    User,
)
from app.chat.services import send_message, start_conversation_with_message
from app.marketplace.services import mark_listing_as_sold, reserve_listing_for_offer
from app.negotiation.services import accept_offer, counter_offer, create_offer, reject_offer


class MarketplaceIntegrationTests(unittest.TestCase):
    def setUp(self) -> None:
        self.app = create_app("testing")
        self.app_context = self.app.app_context()
        self.app_context.push()
        db.create_all()

    def tearDown(self) -> None:
        db.session.remove()
        db.drop_all()
        self.app_context.pop()

    def _create_user(self, email: str) -> User:
        user = User(is_verified=True, email=email, password_hash="hashed")
        db.session.add(user)
        db.session.commit()
        return user

    def _create_listing(self, seller: User) -> Listing:
        category = Category(name="Books", description="Books", is_active=True, display_order=1)
        db.session.add(category)
        db.session.commit()

        listing = Listing(
            sku="SKU-2",
            seller_id=seller.id,
            category_id=category.id,
            title="Textbook",
            slug="textbook-2",
            description="Used textbook",
            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 _create_conversation(self, buyer: User, seller: User, listing: Listing) -> Conversation:
        conversation = Conversation(
            listing_id=listing.id,
            buyer_id=buyer.id,
            seller_id=seller.id,
        )
        db.session.add(conversation)
        db.session.commit()
        return conversation

    def test_accepting_offer_reserves_listing(self) -> None:
        buyer = self._create_user("buyer1@example.com")
        seller = self._create_user("seller1@example.com")
        listing = self._create_listing(seller)
        conversation = self._create_conversation(buyer, seller, listing)

        offer = Offer(
            listing_id=listing.id,
            conversation_id=conversation.id,
            buyer_id=buyer.id,
            seller_id=seller.id,
            amount=Decimal("40.00"),
            status=OfferStatus.PENDING,
        )
        db.session.add(offer)
        db.session.commit()

        accepted_offer = accept_offer(offer, seller)

        self.assertEqual(accepted_offer.status, OfferStatus.ACCEPTED)
        self.assertEqual(listing.status, ListingStatus.RESERVED)

    def test_reserved_listing_rejects_new_offers(self) -> None:
        buyer = self._create_user("buyer2@example.com")
        seller = self._create_user("seller2@example.com")
        listing = self._create_listing(seller)
        conversation = self._create_conversation(buyer, seller, listing)

        offer = Offer(
            listing_id=listing.id,
            conversation_id=conversation.id,
            buyer_id=buyer.id,
            seller_id=seller.id,
            amount=Decimal("35.00"),
            status=OfferStatus.PENDING,
        )
        db.session.add(offer)
        db.session.commit()

        accept_offer(offer, seller)
        with self.assertRaises(Exception):
            create_offer(conversation, buyer, Decimal("42.00"))

    def test_reserved_listing_rejects_new_conversation(self) -> None:
        buyer = self._create_user("buyer3@example.com")
        seller = self._create_user("seller3@example.com")
        listing = self._create_listing(seller)
        conversation = self._create_conversation(buyer, seller, listing)

        offer = Offer(
            listing_id=listing.id,
            conversation_id=conversation.id,
            buyer_id=buyer.id,
            seller_id=seller.id,
            amount=Decimal("30.00"),
            status=OfferStatus.PENDING,
        )
        db.session.add(offer)
        db.session.commit()

        accept_offer(offer, seller)
        with self.assertRaises(Exception):
            start_conversation_with_message(buyer, listing, "Hello")

    def test_seller_can_mark_reserved_listing_as_sold(self) -> None:
        buyer = self._create_user("buyer4@example.com")
        seller = self._create_user("seller4@example.com")
        listing = self._create_listing(seller)
        conversation = self._create_conversation(buyer, seller, listing)

        offer = Offer(
            listing_id=listing.id,
            conversation_id=conversation.id,
            buyer_id=buyer.id,
            seller_id=seller.id,
            amount=Decimal("25.00"),
            status=OfferStatus.PENDING,
        )
        db.session.add(offer)
        db.session.commit()

        accept_offer(offer, seller)
        mark_listing_as_sold(listing, seller)

        self.assertEqual(listing.status, ListingStatus.SOLD)

    def test_sold_listing_disables_bargaining(self) -> None:
        buyer = self._create_user("buyer5@example.com")
        seller = self._create_user("seller5@example.com")
        listing = self._create_listing(seller)
        conversation = self._create_conversation(buyer, seller, listing)

        offer = Offer(
            listing_id=listing.id,
            conversation_id=conversation.id,
            buyer_id=buyer.id,
            seller_id=seller.id,
            amount=Decimal("20.00"),
            status=OfferStatus.PENDING,
        )
        db.session.add(offer)
        db.session.commit()

        accept_offer(offer, seller)
        mark_listing_as_sold(listing, seller)

        with self.assertRaises(Exception):
            counter_offer(offer, seller, Decimal("21.00"))

    def test_negotiation_history_remains_intact(self) -> None:
        buyer = self._create_user("buyer6@example.com")
        seller = self._create_user("seller6@example.com")
        listing = self._create_listing(seller)
        conversation = self._create_conversation(buyer, seller, listing)

        offer = Offer(
            listing_id=listing.id,
            conversation_id=conversation.id,
            buyer_id=buyer.id,
            seller_id=seller.id,
            amount=Decimal("15.00"),
            status=OfferStatus.PENDING,
        )
        db.session.add(offer)
        db.session.commit()

        accept_offer(offer, seller)
        self.assertEqual(db.session.scalar(db.select(db.func.count(Offer.id))), 1)


if __name__ == "__main__":
    unittest.main()
