"""
Tests for _set_reverse_contract_notifications on Owner.

Each test creates a ReverseTracking + Contract pair, calls the method,
and asserts the correct ContractNotification icons are created.
"""

from django.test import TestCase
from eveuniverse.models import EveType

from buybackprogram.models import ContractItem, ContractNotification

from .testdata.factories import (
    AnonymousReverseTrackingFactory,
    ContractFactory,
    LocationWithStructureFactory,
    OwnerFactory,
    ReverseProgramFactory,
    ReverseTrackingFactory,
    ReverseTrackingItemFactory,
)
from .testdata.load_eveuniverse import load_eveuniverse


def _notification_headers(contract):
    """Return the set of notification headers for a contract."""
    return set(
        ContractNotification.objects.filter(contract=contract).values_list(
            "header", flat=True
        )
    )


def _notification_icons(contract):
    """Return the set of notification icons for a contract."""
    return set(
        ContractNotification.objects.filter(contract=contract).values_list(
            "icon", flat=True
        )
    )


class TestReverseContractNotificationsPrice(TestCase):
    """Price mismatch notifications."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.owner = OwnerFactory()
        self.program = ReverseProgramFactory(owner=self.owner)

    def test_no_notification_when_price_matches(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertNotIn("Low payment", headers)
        self.assertNotIn("High payment", headers)

    def test_low_payment_notification_when_player_pays_less(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=80_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertIn("Low payment", headers)

    def test_high_payment_notification_when_player_pays_more(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=120_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertIn("High payment", headers)

    def test_low_payment_icon_is_orange(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=50_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        note = ContractNotification.objects.get(contract=contract, header="Low payment")
        self.assertEqual(note.color, "orange")

    def test_high_payment_icon_is_green(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=150_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        note = ContractNotification.objects.get(
            contract=contract, header="High payment"
        )
        self.assertEqual(note.color, "green")

    def test_price_comparison_truncates_decimals(self):
        """Ensure minor decimal differences don't trigger a mismatch."""
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertNotIn("Low payment", headers)
        self.assertNotIn("High payment", headers)


class TestReverseContractNotificationsItems(TestCase):
    """Item mismatch notifications."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.owner = OwnerFactory()
        self.program = ReverseProgramFactory(owner=self.owner)
        self.eve_type = EveType.objects.filter(published=True).first()

    def test_no_notification_when_items_match(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        ReverseTrackingItemFactory(
            tracking=tracking, eve_type=self.eve_type, quantity=10
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        ContractItem.objects.create(
            contract=contract, eve_type=self.eve_type, quantity=10
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertNotIn("Item mismatch", headers)

    def test_item_mismatch_notification_when_quantities_differ(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        ReverseTrackingItemFactory(
            tracking=tracking, eve_type=self.eve_type, quantity=10
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        # Contract has different quantity
        ContractItem.objects.create(
            contract=contract, eve_type=self.eve_type, quantity=5
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertIn("Item mismatch", headers)

    def test_item_mismatch_notification_when_items_missing_from_contract(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        ReverseTrackingItemFactory(
            tracking=tracking, eve_type=self.eve_type, quantity=10
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        # No ContractItem created — player didn't add items
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertIn("Item mismatch", headers)

    def test_item_mismatch_icon_is_red(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        ReverseTrackingItemFactory(
            tracking=tracking, eve_type=self.eve_type, quantity=10
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        note = ContractNotification.objects.get(
            contract=contract, header="Item mismatch"
        )
        self.assertEqual(note.color, "red")


class TestReverseContractNotificationsLocation(TestCase):
    """Location mismatch notifications."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.owner = OwnerFactory()
        self.location = LocationWithStructureFactory(owner=self.owner)
        self.program = ReverseProgramFactory(owner=self.owner)
        self.program.location.set([self.location])

    def test_no_notification_when_location_matches(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
            start_location_id=self.location.structure_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertNotIn("Location mismatch", headers)

    def test_location_mismatch_notification_when_wrong_structure(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
            start_location_id=9999999999,  # wrong structure
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertIn("Location mismatch", headers)

    def test_location_mismatch_icon_is_red(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
            start_location_id=9999999999,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        note = ContractNotification.objects.get(
            contract=contract, header="Location mismatch"
        )
        self.assertEqual(note.color, "red")


class TestReverseContractNotificationsTitle(TestCase):
    """Title mismatch notifications."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.owner = OwnerFactory()
        self.program = ReverseProgramFactory(owner=self.owner)

    def test_no_notification_when_title_matches(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertNotIn("Title variation", headers)

    def test_title_variation_when_extra_text_in_title(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number + " extra text",
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertNotIn("Title variation", headers)  # tracking_number IS in title

    def test_title_variation_when_completely_wrong_title(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title="completely wrong title",
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertIn("Title variation", headers)

    def test_title_variation_icon_is_orange(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title="wrong title",
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        note = ContractNotification.objects.get(
            contract=contract, header="Title variation"
        )
        self.assertEqual(note.color, "orange")


class TestReverseContractNotificationsPublicUser(TestCase):
    """Public (anonymous) submission notifications."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.owner = OwnerFactory()
        self.program = ReverseProgramFactory(owner=self.owner)

    def test_no_notification_for_authenticated_user(self):
        tracking = ReverseTrackingFactory(program=self.program, net_price=100_000)
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertNotIn("Public submission", headers)

    def test_public_submission_notification_for_anonymous_user(self):
        tracking = AnonymousReverseTrackingFactory(
            program=self.program, net_price=100_000
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertIn("Public submission", headers)

    def test_public_submission_icon_is_globe(self):
        tracking = AnonymousReverseTrackingFactory(
            program=self.program, net_price=100_000
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        note = ContractNotification.objects.get(
            contract=contract, header="Public submission"
        )
        self.assertEqual(note.icon, "fa-globe")


class TestReverseContractNotificationsAdditionalNotes(TestCase):
    """Additional notes from buyer."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.owner = OwnerFactory()
        self.program = ReverseProgramFactory(owner=self.owner)

    def test_no_note_notification_when_no_additional_notes(self):
        tracking = ReverseTrackingFactory(
            program=self.program, net_price=100_000, additional_notes=""
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertNotIn("Note from buyer", headers)

    def test_note_notification_when_additional_notes_present(self):
        tracking = ReverseTrackingFactory(
            program=self.program,
            net_price=100_000,
            additional_notes="Please deliver ASAP",
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        headers = _notification_headers(contract)
        self.assertIn("Note from buyer", headers)

    def test_note_notification_contains_buyer_message(self):
        note_text = "Please deliver ASAP"
        tracking = ReverseTrackingFactory(
            program=self.program,
            net_price=100_000,
            additional_notes=note_text,
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        note = ContractNotification.objects.get(
            contract=contract, header="Note from buyer"
        )
        self.assertIn(note_text, note.message)


class TestReverseContractNotificationsMultiple(TestCase):
    """Multiple notifications can be created in a single call."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.owner = OwnerFactory()
        self.location = LocationWithStructureFactory(owner=self.owner)
        self.program = ReverseProgramFactory(owner=self.owner)
        self.program.location.set([self.location])

    def test_multiple_notifications_created_together(self):
        """Wrong price + wrong location + anonymous = 3 notifications."""
        tracking = AnonymousReverseTrackingFactory(
            program=self.program, net_price=100_000
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=50_000,  # wrong price
            start_location_id=9999999,  # wrong location
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        count = ContractNotification.objects.filter(contract=contract).count()
        self.assertGreaterEqual(count, 3)

    def test_clean_contract_has_no_notifications(self):
        """A perfect contract with no issues should produce zero notifications."""
        tracking = ReverseTrackingFactory(
            program=self.program,
            net_price=100_000,
            additional_notes="",
        )
        contract = ContractFactory(
            title=tracking.tracking_number,
            price=100_000,
            start_location_id=self.location.structure_id,
            assignee_id=self.owner.character.character.character_id,
        )
        self.owner._set_reverse_contract_notifications(tracking, contract)
        count = ContractNotification.objects.filter(contract=contract).count()
        self.assertEqual(count, 0)
