# tests/integration/test_aider_event_repository.py | 2026-04-22 @ Asia/Taipei
"""AiderEventRepository 整合測試 — 使用真實 awoooi_dev PostgreSQL

替換 tests/test_aider_event_processor.py 中違反 feedback_no_mock_testing.md 的
FakeRepo / FakeSession mock。

原測試 (test_aider_event_processor.py) 驗證的是 AiderEventProcessor._process_one()
的整體流程（parse → incident → DB write → ACK），其中：
- FakeRepo / FakeSession → 此整合測試改用真實 DB 驗證 insert 行為
- fake_r.xack (Redis) → Redis 屬外部 broker，仍可在 unit test 中 mock（符合「外部 API」例外）
- fake_engine (IncidentEngine) → AI 推斷服務，屬外部呼叫，仍可 mock

此檔案: 只測「DB 層」— AiderEventRepository.insert() + model_stats_since()
其餘路由邏輯已在 test_aider_event_processor.py 的 parser/ACK 部份覆蓋。

規則: 每個測試後 rollback（由 integration/conftest.py db_session fixture 保證）
禁止 Mock — 直接使用真實 DB 連線。
"""
from __future__ import annotations

from datetime import datetime, timezone, timedelta

import pytest

from src.repositories.aider_event_repository import AiderEventRepository

TAIPEI = timezone(timedelta(hours=8))


def _ts() -> datetime:
    return datetime.now(TAIPEI)


# =============================================================================
# insert() 基本寫入
# =============================================================================

class TestAiderEventRepositoryInsert:
    """驗證 insert() 正確寫入 aider_events 表。"""

    @pytest.mark.asyncio
    async def test_insert_error_event_returns_id(self, db_session):
        """error event 插入後應回傳有效 BIGSERIAL id。"""
        repo = AiderEventRepository(db_session)
        row_id = await repo.insert(
            session_id="s-test-001",
            ts=_ts(),
            type_="error",
            host="ogt-mac",
            payload={"cwd": "/r", "model": "elephant-alpha",
                     "kind": "api_rate_limit", "message": "429",
                     "context_50chars": ""},
        )
        assert isinstance(row_id, int)
        assert row_id > 0

    @pytest.mark.asyncio
    async def test_insert_session_start_with_incident_id(self, db_session):
        """insert 可附帶 incident_id（nullable FK）。"""
        repo = AiderEventRepository(db_session)
        row_id = await repo.insert(
            session_id="s-test-002",
            ts=_ts(),
            type_="session_start",
            host="ogt-mac",
            payload={"cwd": "/r", "model": "elephant-alpha",
                     "aider_args": [], "aider_pid": 1, "cli_version": "0.86"},
            incident_id="INC-20260422-0001",
        )
        assert row_id > 0

    @pytest.mark.asyncio
    async def test_insert_without_incident_id(self, db_session):
        """incident_id 可為 None（常見情境）。"""
        repo = AiderEventRepository(db_session)
        row_id = await repo.insert(
            session_id="s-test-003",
            ts=_ts(),
            type_="commit",
            host="ogt-mac",
            payload={"cwd": "/r", "model": "gemini-pro",
                     "commit_hash": "abc123", "message": "fix: something"},
        )
        assert row_id > 0


# =============================================================================
# count_by_session()
# =============================================================================

class TestAiderEventRepositoryCount:

    @pytest.mark.asyncio
    async def test_count_returns_correct_value(self, db_session):
        """插入 3 筆相同 session_id，count 應回傳 3。"""
        repo = AiderEventRepository(db_session)
        sid = "s-count-test-001"
        for i in range(3):
            await repo.insert(
                session_id=sid,
                ts=_ts(),
                type_="error",
                host="ogt-mac",
                payload={"kind": "test", "message": f"err-{i}",
                         "model": "m", "cwd": "/r", "context_50chars": ""},
            )
        count = await repo.count_by_session(sid)
        assert count == 3

    @pytest.mark.asyncio
    async def test_count_unknown_session_is_zero(self, db_session):
        """不存在的 session_id 應回傳 0。"""
        repo = AiderEventRepository(db_session)
        count = await repo.count_by_session("nonexistent-session-xyz")
        assert count == 0


# =============================================================================
# model_stats_since() — AI Router feedback 聚合查詢
# =============================================================================

class TestAiderEventRepositoryModelStats:

    @pytest.mark.asyncio
    async def test_model_stats_returns_list(self, db_session):
        """model_stats_since() 應回傳 list（即使空）。"""
        repo = AiderEventRepository(db_session)
        result = await repo.model_stats_since(days=1)
        assert isinstance(result, list)

    @pytest.mark.asyncio
    async def test_model_stats_aggregates_correctly(self, db_session):
        """插入 session_start + error，stats 應正確統計 error_rate。

        注意：model_stats_since 聚合邏輯依賴 session_start/session_end payload.model 欄位，
        且需要多筆 session 才能統計。此測試驗證：不崩潰、回傳正確型別。
        """
        repo = AiderEventRepository(db_session)
        ts_now = _ts()

        # 插入一個 session 含 session_start（提供 model 資訊）+ 一筆 error
        sid = "s-stats-test-001"
        await repo.insert(
            session_id=sid, ts=ts_now,
            type_="session_start", host="ogt-mac",
            payload={"cwd": "/awoooi", "model": "elephant-alpha",
                     "aider_args": [], "aider_pid": 1, "cli_version": "0.86"},
        )
        await repo.insert(
            session_id=sid, ts=ts_now,
            type_="error", host="ogt-mac",
            payload={"cwd": "/awoooi", "model": "elephant-alpha",
                     "kind": "api_rate_limit", "message": "429",
                     "context_50chars": ""},
        )
        await db_session.flush()  # flush 使 SQL CTE 能看到資料（不 commit）

        result = await repo.model_stats_since(days=1)
        assert isinstance(result, list)
        # 若有回傳結果，驗證欄位格式正確
        for row in result:
            assert "model" in row
            assert "total" in row
            assert "errors" in row
            assert "success_rate" in row
