"""Tests for contact sorting and last_message_at preservation."""

import builtins
import gettext
import importlib.util
import sys
from datetime import datetime, timedelta
from pathlib import Path
from unittest.mock import MagicMock

if not hasattr(builtins, "_"):
    gettext.install("meshy")

from meshy.models import Contact, ContactType
from meshy.storage import Storage

# contacts_view.py contains pure-Python sort logic but its package __init__
# pulls in GTK/Shumate.  Load the single file directly to skip that chain.
_src = Path(__file__).resolve().parent.parent / "src" / "views" / "contacts_view.py"
_spec = importlib.util.spec_from_file_location("_contacts_view_isolated", _src)
_mod = importlib.util.module_from_spec(_spec)

_GI_STUBS = ["gi", "gi.repository"]
_gi_originals = {m: sys.modules.pop(m, None) for m in _GI_STUBS}
for m in _GI_STUBS:
    sys.modules[m] = MagicMock()
_spec.loader.exec_module(_mod)
for m in _GI_STUBS:
    sys.modules.pop(m, None)
    if _gi_originals[m] is not None:
        sys.modules[m] = _gi_originals[m]

contact_sort_key = _mod.contact_sort_key
SORT_AZ = _mod.SORT_AZ
SORT_HEARD = _mod.SORT_HEARD
SORT_MESSAGES = _mod.SORT_MESSAGES


def _contact(name, *, fav=False, last_msg=None, last_seen=None, ctype=ContactType.CHAT):
    return Contact(
        public_key=bytes(range(32)),
        name=name,
        type=ctype,
        flags=1 if fav else 0,
        last_message_at=last_msg,
        last_seen=last_seen,
    )


# ─── Sort by name (A-Z) ────────────────────────────────────


class TestSortAZ:
    def test_alphabetical_order(self):
        contacts = [_contact("Charlie"), _contact("Alice"), _contact("Bob")]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_AZ, False))
        assert [c.name for c in contacts] == ["Alice", "Bob", "Charlie"]

    def test_case_insensitive(self):
        contacts = [_contact("bob"), _contact("Alice")]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_AZ, False))
        assert [c.name for c in contacts] == ["Alice", "bob"]

    def test_favorites_first(self):
        contacts = [
            _contact("Zara", fav=False),
            _contact("Alice", fav=True),
            _contact("Bob", fav=False),
        ]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_AZ, True))
        assert [c.name for c in contacts] == ["Alice", "Bob", "Zara"]

    def test_favorites_sorted_among_themselves(self):
        contacts = [
            _contact("Zara", fav=True),
            _contact("Alice", fav=True),
            _contact("Bob", fav=False),
        ]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_AZ, True))
        assert [c.name for c in contacts] == ["Alice", "Zara", "Bob"]


# ─── Sort by latest message ────────────────────────────────


class TestSortMessages:
    def test_newest_message_first(self):
        now = datetime.now()
        contacts = [
            _contact("Old", last_msg=now - timedelta(hours=2)),
            _contact("New", last_msg=now),
            _contact("Mid", last_msg=now - timedelta(hours=1)),
        ]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_MESSAGES, False))
        assert [c.name for c in contacts] == ["New", "Mid", "Old"]

    def test_no_message_sorts_last(self):
        now = datetime.now()
        contacts = [
            _contact("NoMsg"),
            _contact("HasMsg", last_msg=now),
        ]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_MESSAGES, False))
        assert [c.name for c in contacts] == ["HasMsg", "NoMsg"]

    def test_favorites_first_with_messages(self):
        now = datetime.now()
        contacts = [
            _contact("NonFav", last_msg=now),
            _contact("Fav", fav=True, last_msg=now - timedelta(hours=5)),
        ]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_MESSAGES, True))
        assert [c.name for c in contacts] == ["Fav", "NonFav"]

    def test_repeater_without_message_below_chat_with_message(self):
        now = datetime.now()
        contacts = [
            _contact("Repeater", fav=True, ctype=ContactType.REPEATER),
            _contact("ChatContact", fav=True, last_msg=now),
        ]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_MESSAGES, True))
        assert [c.name for c in contacts] == ["ChatContact", "Repeater"]


# ─── Sort by last heard ─────────────────────────────────────


class TestSortHeard:
    def test_most_recently_heard_first(self):
        now = datetime.now()
        contacts = [
            _contact("Old", last_seen=now - timedelta(hours=3)),
            _contact("New", last_seen=now),
        ]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_HEARD, False))
        assert [c.name for c in contacts] == ["New", "Old"]

    def test_no_last_seen_sorts_last(self):
        now = datetime.now()
        contacts = [
            _contact("NeverSeen"),
            _contact("Seen", last_seen=now),
        ]
        contacts.sort(key=lambda c: contact_sort_key(c, SORT_HEARD, False))
        assert [c.name for c in contacts] == ["Seen", "NeverSeen"]


# ─── last_message_at preservation (storage) ─────────────────


class TestStorageLastMessageAtPreservation:
    def setup_method(self):
        self.storage = Storage(db_path=":memory:")

    def teardown_method(self):
        self.storage.close()

    def test_save_contact_preserves_last_message_at_via_coalesce(self):
        now = datetime.now()
        c = Contact(
            public_key=bytes(range(32)),
            name="Alice",
            type=ContactType.CHAT,
            last_message_at=now,
        )
        self.storage.save_contact(c)

        c_update = Contact(
            public_key=bytes(range(32)),
            name="Alice",
            type=ContactType.CHAT,
            last_message_at=None,
        )
        self.storage.save_contact(c_update)

        loaded = self.storage.get_contacts()
        alice = [x for x in loaded if x.name == "Alice"][0]
        assert alice.last_message_at is not None
        assert abs(alice.last_message_at.timestamp() - now.timestamp()) < 1

    def test_save_contact_updates_last_message_at_when_provided(self):
        old = datetime(2025, 1, 1)
        new = datetime(2026, 6, 15)
        c = Contact(
            public_key=bytes(range(32)),
            name="Bob",
            type=ContactType.CHAT,
            last_message_at=old,
        )
        self.storage.save_contact(c)

        c.last_message_at = new
        self.storage.save_contact(c)

        loaded = self.storage.get_contacts()
        bob = [x for x in loaded if x.name == "Bob"][0]
        assert abs(bob.last_message_at.timestamp() - new.timestamp()) < 1
