# backend/tests/test_oauth_mock.py — C3 OAuth 플로우 + C8 토큰 만료
# (respx 전역상태 플레이크 회피 위해 httpx.post 를 직접 monkeypatch — 결정적)
import time

import pytest

from app.config import get_settings
from app.connectors import oauth
from app.crypto import decrypt_token, encrypt_token
from app.models import ConnectorAccount, ConnectorDomain, ConnState


class _FakeResp:
    def __init__(self, status, payload=None):
        self.status_code = status
        self._payload = payload or {}

    def json(self):
        return self._payload

    def raise_for_status(self):
        if self.status_code >= 400:
            raise RuntimeError(f"http {self.status_code}")


@pytest.fixture()
def google_creds(monkeypatch):
    monkeypatch.setenv("GOOGLE_CLIENT_ID", "cid")
    monkeypatch.setenv("GOOGLE_CLIENT_SECRET", "sec")
    get_settings.cache_clear()
    yield
    get_settings.cache_clear()


def test_oauth_callback_stores_encrypted_token(session, google_creds, monkeypatch):
    s, _ = session
    url = oauth.start_oauth(s, "mail", "gmail")
    assert "code_challenge=" in url and "state=" in url  # PKCE + state
    state = url.split("state=")[1].split("&")[0]

    # 평문 노출 검사가 결정적이도록 충분히 distinctive 한 토큰 사용(짧은 'AT'는 base64에 우연히 등장).
    access = "ACCESS-TOKEN-Zx9Q7w"
    monkeypatch.setattr(
        oauth.httpx,
        "post",
        lambda *a, **k: _FakeResp(
            200,
            {
                "access_token": access,
                "refresh_token": "RT",
                "expires_in": 3600,
                "scope": "https://www.googleapis.com/auth/gmail.readonly",
            },
        ),
    )
    # _fetch_identity 의 실 네트워크 호출 차단(이메일 없음 → 단일 계정 폴백).
    monkeypatch.setattr(oauth.httpx, "get", lambda *a, **k: _FakeResp(200, {"emailAddress": ""}))
    acct = oauth.finish_oauth(s, code="CODE", state=state)
    assert acct.mode == "real" and acct.state == ConnState.connected
    assert acct.token_enc and access not in acct.token_enc  # 평문 노출 금지
    assert decrypt_token(acct.token_enc)["access_token"] == access
    from app.models import OAuthState

    assert s.get(OAuthState, state) is None  # state row 소거


def test_token_refresh_failure_marks_expired(session, google_creds, monkeypatch):
    s, _ = session
    acct = ConnectorAccount(
        id="ca-mail-gmail",
        domain=ConnectorDomain.mail,
        provider="gmail",
        token_enc=encrypt_token(
            {"access_token": "old", "refresh_token": "RT", "expires_at": int(time.time()) - 10}
        ),
    )
    s.add(acct)
    s.commit()
    monkeypatch.setattr(oauth.httpx, "post", lambda *a, **k: _FakeResp(401))
    with pytest.raises(PermissionError):
        oauth.valid_access_token(s, acct)
    assert acct.state == ConnState.token_expired


def test_token_valid_when_unexpired(session, google_creds):
    s, _ = session
    acct = ConnectorAccount(
        id="ca-x",
        domain=ConnectorDomain.mail,
        provider="gmail",
        token_enc=encrypt_token({"access_token": "FRESH", "expires_at": int(time.time()) + 9999}),
    )
    assert oauth.valid_access_token(s, acct) == "FRESH"
