import json

from django.test import Client, TestCase
from django.urls import reverse

from shop.models import FittedListing, Order, Shop
from shop.tests._corptools import make_owner_character, make_ship_type


class FittedStorefrontTests(TestCase):
    def setUp(self):
        self.user, self.char = make_owner_character()
        self.client = Client()
        self.shop = Shop.objects.create(
            slug="s",
            name="S",
            created_by=self.user,
            is_published=True,
            enabled_sections=["fitted"],
        )
        self.hull = make_ship_type(type_id=23773, name="Ragnarok")
        self.listing = FittedListing.objects.create(
            shop=self.shop,
            eve_type=self.hull,
            item_id=9001,
            ship_name="Doomsday",
            price=90_000_000_000,
            fit_json={
                "slots": [
                    {
                        "flag": "HiSlot0",
                        "type_id": 3057,
                        "name": "Gun",
                        "charge": {"type_id": 12608, "name": "Antimatter L"},
                    }
                ],
                "drones": [{"type_id": 2456, "name": "Ogre II", "qty": 5}],
                "cargo": [],
                "manifest": [
                    {
                        "flag": "ShipHangar",
                        "type_id": 28352,
                        "name": "Rorqual",
                        "qty": 2,
                    }
                ],
            },
        )

    def test_section_page_renders_ships(self):
        url = reverse("storefront:section", args=[self.shop.slug, "fitted"])
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, "Doomsday")
        self.assertContains(resp, "Ragnarok")

    def test_detail_page_renders_fit_and_eft(self):
        url = reverse("storefront:fitted_item", args=[self.shop.slug, self.listing.pk])
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, "Gun")  # fitting item
        self.assertContains(resp, "Antimatter L")  # charge
        self.assertContains(resp, "Ship Maintenance Bay")  # manifest label
        self.assertContains(resp, "[Ragnarok, Doomsday]")  # EFT header

    def test_order_caps_quantity_at_one(self):
        # a logged-in buyer requesting qty 5 of a fitted ship gets 1
        session = self.client.session
        session["shop_buyer"] = {"id": 123, "name": "Buyer"}
        session.save()
        url = reverse("storefront:submit_order", args=[self.shop.slug])
        resp = self.client.post(
            url,
            data=json.dumps(
                {
                    "lines": [
                        {"listing_id": self.listing.pk, "section": "fitted", "qty": 5}
                    ]
                }
            ),
            content_type="application/json",
        )
        self.assertEqual(resp.status_code, 200)
        order = Order.objects.get(reference=resp.json()["reference"])
        line = order.lines.get()
        self.assertEqual(line.quantity, 1)
        self.assertEqual(line.label, "Doomsday")
        self.assertEqual(line.section, "fitted")
        # the fit is frozen onto the order line (survives the listing being removed)
        self.assertEqual(line.fit_json["slots"][0]["type_id"], 3057)

    def test_detail_page_survives_empty_fit_json(self):
        # a listing whose fit hasn't been reconstructed yet (fit_json={}) must render,
        # not 500 in the EFT serializer
        bare = FittedListing.objects.create(
            shop=self.shop,
            eve_type=self.hull,
            item_id=9002,
            ship_name="Bare",
            fit_json={},
        )
        url = reverse("storefront:fitted_item", args=[self.shop.slug, bare.pk])
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, "[Ragnarok, Bare]")  # EFT header only

    def test_malicious_ship_name_is_escaped(self):
        FittedListing.objects.create(
            shop=self.shop,
            eve_type=self.hull,
            item_id=9003,
            ship_name="<img src=x onerror=alert(1)>",
            fit_json={},
        )
        url = reverse("storefront:section", args=[self.shop.slug, "fitted"])
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertNotContains(resp, "<img src=x onerror=alert(1)>")
        self.assertContains(resp, "&lt;img src=x onerror=alert(1)&gt;")

    def test_detail_404_when_section_disabled(self):
        # disabling the fitted section must hide the fit + EFT, not just the nav
        self.shop.enabled_sections = ["hulls"]
        self.shop.save()
        url = reverse("storefront:fitted_item", args=[self.shop.slug, self.listing.pk])
        self.assertEqual(self.client.get(url).status_code, 404)

    def test_non_numeric_listing_id_is_bad_request_not_500(self):
        session = self.client.session
        session["shop_buyer"] = {"id": 123, "name": "Buyer"}
        session.save()
        url = reverse("storefront:submit_order", args=[self.shop.slug])
        resp = self.client.post(
            url,
            data=json.dumps(
                {
                    "lines": [
                        {"listing_id": "not-a-number", "section": "fitted", "qty": 1}
                    ]
                }
            ),
            content_type="application/json",
        )
        self.assertEqual(resp.status_code, 400)
