"""phone_store: typed-memory CRUD + SMS matching. Memory HTTP is a fake
in-memory store — no network."""
import phone_store


class FakeMem:
    """Minimal stand-in for the memory service: /remember, /memories, /forget."""

    def __init__(self):
        self.rows = []
        self._n = 0

    async def request(self, method, path, params=None, body=None, timeout=20.0):
        if path == "/remember":
            self._n += 1
            mid = f"m{self._n}"
            self.rows.append({"id": mid, "fact": body["text"],
                              "kind": body.get("kind"),
                              "group": body.get("group")})
            return {"id": mid}
        if path == "/memories":
            grp = params.get("group")
            kind = params.get("kind")
            out = [r for r in reversed(self.rows)         # newest-first
                   if r["group"] == grp
                   and (kind is None or r["kind"] == kind)]
            return {"memories": out}
        if path == "/forget":
            bid = body["id"]
            self.rows = [r for r in self.rows if r["id"] != bid]
            return {"ok": True}
        return {}


def _wire(monkeypatch):
    fake = FakeMem()
    monkeypatch.setattr(phone_store, "_mem_request", fake.request)
    return fake


def test_phone_group_default_and_override(monkeypatch):
    monkeypatch.setenv("MEMORY_GROUP", "ahmed")
    monkeypatch.delenv("PHONE_STORE_GROUP", raising=False)
    assert phone_store.phone_group() == "ahmed_phone"
    monkeypatch.setenv("PHONE_STORE_GROUP", "custom_ns")
    assert phone_store.phone_group() == "custom_ns"


async def test_put_get_list_place(monkeypatch):
    _wire(monkeypatch)
    await phone_store.put_item(phone_store.KIND_PLACE,
                               {"label": "work office", "address": "King Fahd Rd"})
    got = await phone_store.get_item(phone_store.KIND_PLACE, "WORK OFFICE")
    assert got and got["address"] == "King Fahd Rd"
    assert got["_id"]
    items = await phone_store.list_items(phone_store.KIND_PLACE)
    assert len(items) == 1


async def test_put_is_upsert_by_key(monkeypatch):
    _wire(monkeypatch)
    await phone_store.put_item(phone_store.KIND_CONTACT,
                               {"name": "father", "number": "0500000001"})
    await phone_store.put_item(phone_store.KIND_CONTACT,
                               {"name": "Father", "number": "0500000002"})
    items = await phone_store.list_items(phone_store.KIND_CONTACT)
    assert len(items) == 1                       # replaced, not duplicated
    assert items[0]["number"] == "0500000002"


async def test_delete_item(monkeypatch):
    _wire(monkeypatch)
    await phone_store.put_item(phone_store.KIND_SMS_WATCH, {"sender": "Barq"})
    n = await phone_store.delete_item(phone_store.KIND_SMS_WATCH, "barq")
    assert n == 1
    assert await phone_store.list_items(phone_store.KIND_SMS_WATCH) == []


async def test_put_isolated_by_kind(monkeypatch):
    _wire(monkeypatch)
    await phone_store.put_item(phone_store.KIND_PLACE,
                               {"label": "gym", "address": "A"})
    await phone_store.put_item(phone_store.KIND_CONTACT,
                               {"name": "gym", "number": "0501112222"})
    # same key text across kinds must not collide
    assert len(await phone_store.list_items(phone_store.KIND_PLACE)) == 1
    assert len(await phone_store.list_items(phone_store.KIND_CONTACT)) == 1


async def test_put_requires_key_field(monkeypatch):
    _wire(monkeypatch)
    try:
        await phone_store.put_item(phone_store.KIND_PLACE, {"address": "x"})
    except ValueError:
        return
    assert False, "put_item must reject a missing key field"


def test_sms_matches_exact_substring_and_wildcard():
    assert phone_store.sms_matches("SNB-AlAhli", ["SNB-AlAhli"])
    assert phone_store.sms_matches("SNB-AlAhli", ["snb"])       # substring
    assert phone_store.sms_matches("anything", ["*"])           # wildcard
    assert not phone_store.sms_matches("Barq", ["SNB", "Aramex"])
    assert not phone_store.sms_matches("", ["*"])               # empty sender
    assert not phone_store.sms_matches("Barq", [])              # empty list
