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.negotiation.services import accept_offer, counter_offer, reject_offer


class NegotiationPhase2Tests(unittest.TestCase):
    def setUp(self) -> None:
        self.app = create_app("testing")
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.assertIn("offers", db.metadata.tables)
        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-1",
            seller_id=seller.id,
            category_id=category.id,
            title="Textbook",
            slug="textbook",
            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_seller_can_accept_pending_offer_and_notify_buyer(self) -> None:
        buyer = self._create_user("buyer@example.com")
        seller = self._create_user("seller@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"),
            optional_message="Please take it",
            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(offer.status, OfferStatus.ACCEPTED)
        self.assertEqual(
            db.session.scalar(db.select(db.func.count(Notification.id)).where(Notification.user_id == buyer.id)),
            1,
        )

    def test_counter_offer_creates_new_pending_offer_and_counters_old(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"),
            optional_message="Counter this",
            status=OfferStatus.PENDING,
        )
        db.session.add(offer)
        db.session.commit()

        countered_offer = counter_offer(offer, seller, Decimal("42.00"), "New offer")

        self.assertEqual(offer.status, OfferStatus.COUNTERED)
        self.assertEqual(countered_offer.status, OfferStatus.PENDING)
        self.assertEqual(countered_offer.buyer_id, seller.id)
        self.assertEqual(countered_offer.seller_id, buyer.id)
        self.assertEqual(countered_offer.amount, Decimal("42.00"))
        self.assertEqual(
            db.session.scalar(db.select(db.func.count(Offer.id)).where(Offer.conversation_id == conversation.id)),
            2,
        )

    def test_reject_offer_marks_offer_rejected(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("18.00"),
            status=OfferStatus.PENDING,
        )
        db.session.add(offer)
        db.session.commit()

        rejected_offer = reject_offer(offer, seller)

        self.assertEqual(rejected_offer.status, OfferStatus.REJECTED)
        self.assertEqual(offer.status, OfferStatus.REJECTED)


if __name__ == "__main__":
    unittest.main()
