# backend/tests/test_federation_real.py — C7 real(가짜) sync → event_bus 발행
# 같은 코드 경로(GmailConnector.normalize/write/sync)로 mail.received 가 실제 발행됨을 증명.
from app import seed as seedmod
from app.automation.event_bus import bus
from app.connectors.base import RawRecord
from app.connectors.mail.real_gmail import GmailConnector
from app.models import ConnectorAccount


class FakeGmailConnector(GmailConnector):
    """network 없이 fetch 만 가짜로 — normalize/write/sync 는 real 코드 그대로."""

    payloads: list = []

    def fetch(self, session, *, full=False):
        for p in self.payloads:
            yield RawRecord(
                external_id=p["id"],
                payload=p,
                etag=str(p.get("historyId", "")),
                external_updated_at=None,
            )


_MSG = {
    "id": "gmail-real-1",
    "historyId": "9001",
    "labelIds": ["UNREAD", "IMPORTANT"],
    "snippet": "현우님 온보딩 시안 v3 도착",
    "internalDate": "1718000000000",
    "payload": {
        "headers": [
            {"name": "From", "value": "hyunwoo@lumi.co"},
            {"name": "Subject", "value": "온보딩 시안 v3"},
        ]
    },
}


def test_real_sync_publishes_mail_received_once(session):
    s, _ = session
    seedmod.run_seed(session=s, reset=True)
    acct = s.get(ConnectorAccount, "ca-mail-personal")

    before = len([e for e in bus.history if e.type == "mail.received"])
    conn = FakeGmailConnector(acct)
    conn.payloads = [_MSG]
    r1 = conn.sync(s)
    assert r1.upserted == 1 and r1.events_published == ["mail.received"]

    # 같은 historyId(etag) 재공급 → 멱등(이벤트 0)
    conn2 = FakeGmailConnector(acct)
    conn2.payloads = [_MSG]
    r2 = conn2.sync(s)
    assert r2.upserted == 0 and r2.events_published == []

    after = len([e for e in bus.history if e.type == "mail.received"])
    assert after - before == 1  # 새 메일당 정확히 1회


def test_real_sync_failure_does_not_crash(session):
    s, _ = session
    seedmod.run_seed(session=s, reset=True)
    acct = s.get(ConnectorAccount, "ca-mail-personal")

    class BoomConnector(GmailConnector):
        def fetch(self, session, *, full=False):
            raise RuntimeError("network down")

    res = BoomConnector(acct).sync(s)  # 예외가 앱을 멈추지 않음
    assert res.errors == 1 and "network down" in res.detail
    s.refresh(acct)
    assert acct.state == "error"
