"""Order model tests."""

from django.test import TestCase

from shop.models import (
    BpcListing,
    BpcVariant,
    Buyer,
    HullListing,
    Order,
    OrderLine,
    Shop,
)
from shop.models.orders import REF_ALPHABET
from shop.sections import handler_for
from shop.tests.test_stock import make_owner_character, make_ship_type


class BuyerTests(TestCase):
    def test_upsert_creates_then_updates(self):
        b1 = Buyer.upsert_from_session({"id": 42, "name": "Alice"})
        self.assertEqual(b1.character_name, "Alice")
        b2 = Buyer.upsert_from_session(
            {"id": 42, "name": "Alice Renamed"}, discord_handle="alice"
        )
        self.assertEqual(b1.pk, b2.pk)
        self.assertEqual(b2.character_name, "Alice Renamed")
        self.assertEqual(b2.discord_handle, "alice")
        self.assertEqual(Buyer.objects.count(), 1)


class OrderTests(TestCase):
    def setUp(self):
        self.user, _ = make_owner_character()
        self.shop = Shop.objects.create(slug="s", name="S", created_by=self.user)
        self.buyer = Buyer.objects.create(character_id=1, character_name="B")
        self.t1 = make_ship_type(type_id=101, name="Alpha")
        self.t2 = make_ship_type(type_id=102, name="Bravo")

    def test_reference_generated_from_safe_alphabet(self):
        o = Order.objects.create(shop=self.shop, buyer=self.buyer)
        self.assertEqual(len(o.reference), 5)
        self.assertTrue(all(c in REF_ALPHABET for c in o.reference))
        for ambiguous in "01ILO":
            self.assertNotIn(ambiguous, o.reference)

    def test_reference_kept_on_resave(self):
        o = Order.objects.create(shop=self.shop, buyer=self.buyer)
        ref = o.reference
        o.status = Order.ACCEPTED
        o.save()
        self.assertEqual(o.reference, ref)

    def test_estimated_total_and_has_ask(self):
        o = Order.objects.create(shop=self.shop, buyer=self.buyer)
        OrderLine.objects.create(
            order=o, section="hulls", eve_type=self.t1, quantity=2, unit_price=100
        )
        OrderLine.objects.create(
            order=o, section="hulls", eve_type=self.t2, quantity=1, unit_price=0
        )
        self.assertEqual(o.estimated_total, 200)
        self.assertTrue(o.has_ask)

    def test_line_total(self):
        o = Order.objects.create(shop=self.shop, buyer=self.buyer)
        line = OrderLine.objects.create(
            order=o,
            section="hulls",
            eve_type=self.t1,
            quantity=3,
            unit_price=50,
            listing_id=999,
        )
        self.assertEqual(line.line_total, 150)
        self.assertEqual(line.listing_id, 999)


class BuildOrderLineTests(TestCase):
    def setUp(self):
        self.user, _ = make_owner_character()
        self.shop = Shop.objects.create(
            slug="s",
            name="S",
            created_by=self.user,
            is_published=True,
            enabled_sections=["hulls"],
        )
        self.ship = make_ship_type()
        self.listing = HullListing.objects.create(
            shop=self.shop, eve_type=self.ship, price=500, stock=1
        )

    def test_valid_listing(self):
        data = handler_for("hulls").build_order_line(self.shop, self.listing.pk)
        self.assertEqual(data["eve_type"], self.ship)
        self.assertEqual(data["unit_price"], 500)
        self.assertEqual(data["section"], "hulls")
        self.assertEqual(data["listing_id"], self.listing.pk)

    def test_missing_or_foreign_returns_none(self):
        other = Shop.objects.create(slug="o", name="O", created_by=self.user)
        self.assertIsNone(handler_for("hulls").build_order_line(other, self.listing.pk))
        self.assertIsNone(handler_for("hulls").build_order_line(self.shop, 999999))

    def test_hull_line_has_blank_label(self):
        data = handler_for("hulls").build_order_line(self.shop, self.listing.pk)
        self.assertEqual(data["label"], "")


class BpcBuildOrderLineTests(TestCase):
    def setUp(self):
        self.user, _ = make_owner_character()
        self.shop = Shop.objects.create(
            slug="b",
            name="B",
            created_by=self.user,
            is_published=True,
            enabled_sections=["bpc"],
        )
        self.bp = make_ship_type(type_id=12040, name="Rifter Blueprint")
        self.listing = BpcListing.objects.create(shop=self.shop, eve_type=self.bp)
        self.variant = BpcVariant.objects.create(
            listing=self.listing,
            me=10,
            te=20,
            runs=50,
            stock=2,
            price=8_000_000,
            price_per_run=160_000,
        )

    def test_valid_variant(self):
        data = handler_for("bpc").build_order_line(self.shop, self.variant.pk)
        self.assertEqual(data["section"], "bpc")
        self.assertEqual(data["eve_type"], self.bp)
        self.assertEqual(data["unit_price"], 8_000_000)
        self.assertEqual(data["listing_id"], self.variant.pk)
        self.assertIn("ME 10", data["label"])
        self.assertIn("50", data["label"])

    def test_hidden_or_foreign_returns_none(self):
        hidden = BpcVariant.objects.create(
            listing=self.listing, me=0, te=0, runs=1, price=0, hidden=True
        )
        self.assertIsNone(handler_for("bpc").build_order_line(self.shop, hidden.pk))
        other = Shop.objects.create(slug="o2", name="O2", created_by=self.user)
        self.assertIsNone(handler_for("bpc").build_order_line(other, self.variant.pk))
        self.assertIsNone(handler_for("bpc").build_order_line(self.shop, 999999))


class OrderStatusTests(TestCase):
    def setUp(self):
        self.user, _ = make_owner_character()
        self.shop = Shop.objects.create(slug="st", name="St", created_by=self.user)
        self.buyer = Buyer.objects.create(character_id=1, character_name="B")
        self.t = make_ship_type()

    def test_is_open(self):
        cases = [
            (Order.PENDING, True),
            (Order.ACCEPTED, True),
            (Order.COMPLETED, False),
            (Order.DECLINED, False),
            (Order.CANCELLED, False),
        ]
        for status, expected in cases:
            o = Order.objects.create(shop=self.shop, buyer=self.buyer, status=status)
            self.assertEqual(o.is_open, expected)

    def test_shop_open_order_count(self):
        Order.objects.create(shop=self.shop, buyer=self.buyer, status=Order.PENDING)
        Order.objects.create(shop=self.shop, buyer=self.buyer, status=Order.ACCEPTED)
        Order.objects.create(shop=self.shop, buyer=self.buyer, status=Order.COMPLETED)
        self.assertEqual(self.shop.open_order_count, 2)

    def test_display_helpers(self):
        o = Order.objects.create(shop=self.shop, buyer=self.buyer)
        firm = OrderLine.objects.create(
            order=o, section="hulls", eve_type=self.t, quantity=2, unit_price=1_000_000
        )
        ask = OrderLine.objects.create(
            order=o, section="hulls", eve_type=self.t, quantity=1, unit_price=0
        )
        self.assertEqual(firm.unit_price_display, "1M ISK")
        self.assertEqual(firm.line_total_display, "2M ISK")
        self.assertEqual(ask.unit_price_display, "Ask")
        self.assertEqual(o.estimated_total_display, "2M ISK")
