"""get_teams_connection reads a tenant's Microsoft Teams OAuth connection
from the shared integrations table, refreshing the access token first if
expired. Fully isolated -- get_db_connection and httpx are mocked in every
test.
"""
from datetime import datetime, timedelta, timezone
from unittest.mock import MagicMock, patch

from app.services.integrations.teams import get_teams_connection


def _mock_row(monkeypatch, row):
    conn = MagicMock()
    cur = conn.cursor.return_value.__enter__.return_value
    cur.fetchone.return_value = row
    monkeypatch.setattr(
        "app.services.integrations.teams.get_db_connection", lambda: conn)
    return conn, cur


def test_no_row_means_not_connected(monkeypatch):
    _mock_row(monkeypatch, None)
    assert get_teams_connection("org_test") is None


def test_a_still_valid_token_is_used_without_refreshing(monkeypatch):
    future = datetime.now(timezone.utc) + timedelta(hours=1)
    _mock_row(monkeypatch, ("access-tok", "refresh-tok", future))

    settings_response = MagicMock()
    settings_response.raise_for_status.return_value = None
    settings_response.json.return_value = {"timeZone": "Pacific Standard Time"}
    me_response = MagicMock()
    me_response.raise_for_status.return_value = None
    me_response.json.return_value = {"mail": "shivraj@galaxiq.ai"}

    with patch("httpx.get", side_effect=[settings_response, me_response]) as get, \
         patch("httpx.post") as post:
        connection = get_teams_connection("org_test")

    post.assert_not_called()
    assert get.call_count == 2
    assert connection == {
        "access_token": "access-tok",
        "user_email": "shivraj@galaxiq.ai",
        "timezone": "Pacific Standard Time",
    }


def test_an_expired_token_is_refreshed_before_use(monkeypatch):
    past = datetime.now(timezone.utc) - timedelta(minutes=5)
    conn, cur = _mock_row(monkeypatch, ("stale-tok", "refresh-tok", past))
    monkeypatch.setattr(
        "app.services.integrations.teams.settings.MS_TEAMS_CLIENT_ID", "client-id")
    monkeypatch.setattr(
        "app.services.integrations.teams.settings.MS_TEAMS_CLIENT_SECRET", "client-secret")

    refresh_response = MagicMock()
    refresh_response.raise_for_status.return_value = None
    refresh_response.json.return_value = {"access_token": "fresh-tok", "expires_in": 3600}
    settings_response = MagicMock()
    settings_response.raise_for_status.return_value = None
    settings_response.json.return_value = {"timeZone": "Pacific Standard Time"}
    me_response = MagicMock()
    me_response.raise_for_status.return_value = None
    me_response.json.return_value = {"mail": "shivraj@galaxiq.ai"}

    with patch("httpx.post", return_value=refresh_response) as post, \
         patch("httpx.get", side_effect=[settings_response, me_response]):
        connection = get_teams_connection("org_test")

    post.assert_called_once()
    assert post.call_args.kwargs["data"]["refresh_token"] == "refresh-tok"
    assert connection["access_token"] == "fresh-tok"


def test_a_refresh_failure_is_treated_as_not_connected(monkeypatch):
    past = datetime.now(timezone.utc) - timedelta(minutes=5)
    _mock_row(monkeypatch, ("stale-tok", "revoked-refresh-tok", past))
    refresh_response = MagicMock()
    refresh_response.raise_for_status.side_effect = Exception("invalid_grant")
    with patch("httpx.post", return_value=refresh_response):
        assert get_teams_connection("org_test") is None


def test_a_db_error_is_treated_as_not_connected(monkeypatch):
    conn = MagicMock()
    conn.cursor.side_effect = Exception("connection refused")
    monkeypatch.setattr(
        "app.services.integrations.teams.get_db_connection", lambda: conn)
    assert get_teams_connection("org_test") is None


def test_a_get_db_connection_failure_is_treated_as_not_connected(monkeypatch):
    # get_db_connection() itself can raise on real connection failure (after retries).
    # This must be caught and returned as None, not raised.
    monkeypatch.setattr(
        "app.services.integrations.teams.get_db_connection",
        MagicMock(side_effect=Exception("connection refused")))
    assert get_teams_connection("org_test") is None


def test_a_get_db_connection_failure_during_refresh_persist_is_treated_as_not_failed(monkeypatch):
    # When _refresh fails to persist the refreshed token due to DB connectivity,
    # it should still return the new access_token so the caller can use it
    # immediately (rather than waiting for the next token to be refreshed).
    past = datetime.now(timezone.utc) - timedelta(minutes=5)
    _mock_row(monkeypatch, ("stale-tok", "refresh-tok", past))
    monkeypatch.setattr(
        "app.services.integrations.teams.settings.MS_TEAMS_CLIENT_ID", "client-id")
    monkeypatch.setattr(
        "app.services.integrations.teams.settings.MS_TEAMS_CLIENT_SECRET", "client-secret")

    refresh_response = MagicMock()
    refresh_response.raise_for_status.return_value = None
    refresh_response.json.return_value = {"access_token": "fresh-tok", "expires_in": 3600}
    settings_response = MagicMock()
    settings_response.raise_for_status.return_value = None
    settings_response.json.return_value = {"timeZone": "Pacific Standard Time"}
    me_response = MagicMock()
    me_response.raise_for_status.return_value = None
    me_response.json.return_value = {"mail": "shivraj@galaxiq.ai"}

    call_count = [0]

    def get_db_connection_side_effect():
        # First call succeeds (to read the connection)
        # Second call (during _refresh's persist) fails
        call_count[0] += 1
        if call_count[0] == 1:
            conn = MagicMock()
            cur = conn.cursor.return_value.__enter__.return_value
            cur.fetchone.return_value = ("stale-tok", "refresh-tok", past)
            return conn
        else:
            raise Exception("connection refused during persist")

    mock_get_db = MagicMock(side_effect=get_db_connection_side_effect)
    monkeypatch.setattr(
        "app.services.integrations.teams.get_db_connection",
        mock_get_db)

    with patch("httpx.post", return_value=refresh_response) as post, \
         patch("httpx.get", side_effect=[settings_response, me_response]):
        connection = get_teams_connection("org_test")

    # Even though persist failed, we got the fresh token and can use it
    assert connection is not None
    assert connection["access_token"] == "fresh-tok"
