"""API integration tests for trading report endpoints.

Tests GET /api/reports (list with pagination/filtering) and
GET /api/reports/{report_id} (detail with full report_data).

Uses httpx.AsyncClient with the FastAPI app and mocks the module-level
``pool`` variable in services.api.app.

Requirements validated: 5.4, 5.5, 5.6
"""
from __future__ import annotations

import uuid
from datetime import date, datetime, timezone
from unittest.mock import AsyncMock, patch

import httpx
import pytest

from services.api.app import app

# ── Helpers ──────────────────────────────────────────────────────────────


class FakeRecord(dict):
    """Dict subclass that behaves like an asyncpg Record for bracket access."""

    def __getattr__(self, name: str):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)


def _make_list_record(**overrides) -> FakeRecord:
    """Build a FakeRecord matching the list-endpoint SELECT columns."""
    defaults = {
        "id": uuid.uuid4(),
        "report_type": "daily",
        "period_start": date(2025, 1, 15),
        "period_end": date(2025, 1, 15),
        "validation_status": "passed",
        "generated_at": datetime(2025, 1, 15, 21, 30, tzinfo=timezone.utc),
    }
    defaults.update(overrides)
    return FakeRecord(**defaults)


def _make_detail_record(**overrides) -> FakeRecord:
    """Build a FakeRecord matching the detail-endpoint SELECT columns."""
    defaults = {
        "id": uuid.uuid4(),
        "report_type": "daily",
        "period_start": date(2025, 1, 15),
        "period_end": date(2025, 1, 15),
        "report_data": {
            "pnl": {"realized_pnl": 125.50, "unrealized_pnl": -30.20},
            "executive_summary": "Test summary",
        },
        "validation_status": "passed",
        "generated_at": datetime(2025, 1, 15, 21, 30, tzinfo=timezone.utc),
        "created_at": datetime(2025, 1, 15, 21, 30, 5, tzinfo=timezone.utc),
    }
    defaults.update(overrides)
    return FakeRecord(**defaults)


_POOL_PATCH = "services.api.app.pool"


# ═══════════════════════════════════════════════════════════════════════
# 1. GET /api/reports — list endpoint
# Requirements validated: 5.4, 5.6
# ═══════════════════════════════════════════════════════════════════════


class TestListReports:
    """Tests for GET /api/reports."""

    @pytest.mark.asyncio
    async def test_default_pagination(self) -> None:
        """List reports with no params returns rows using default limit/offset."""
        r1 = _make_list_record()
        r2 = _make_list_record(
            report_type="weekly",
            period_start=date(2025, 1, 13),
            period_end=date(2025, 1, 17),
        )
        mock_pool = AsyncMock()
        mock_pool.fetch = AsyncMock(return_value=[r1, r2])

        with patch(_POOL_PATCH, mock_pool):
            async with httpx.AsyncClient(
                transport=httpx.ASGITransport(app=app), base_url="http://test"
            ) as client:
                resp = await client.get("/api/reports")

        assert resp.status_code == 200
        data = resp.json()
        assert len(data) == 2
        # UUID fields are serialized as strings
        assert data[0]["id"] == str(r1["id"])
        assert data[0]["report_type"] == "daily"
        assert data[0]["period_start"] == "2025-01-15"
        assert data[0]["period_end"] == "2025-01-15"
        assert data[0]["validation_status"] == "passed"
        assert "generated_at" in data[0]

        # pool.fetch called with default limit=20, offset=0
        call_args = mock_pool.fetch.call_args
        sql = call_args[0][0]
        assert "LIMIT" in sql
        assert "OFFSET" in sql
        # Last two positional args are limit and offset
        assert call_args[0][-2] == 20
        assert call_args[0][-1] == 0

    @pytest.mark.asyncio
    async def test_filter_by_report_type(self) -> None:
        """Filtering by report_type=weekly passes the value to the query."""
        r1 = _make_list_record(report_type="weekly")
        mock_pool = AsyncMock()
        mock_pool.fetch = AsyncMock(return_value=[r1])

        with patch(_POOL_PATCH, mock_pool):
            async with httpx.AsyncClient(
                transport=httpx.ASGITransport(app=app), base_url="http://test"
            ) as client:
                resp = await client.get("/api/reports", params={"report_type": "weekly"})

        assert resp.status_code == 200
        data = resp.json()
        assert len(data) == 1
        assert data[0]["report_type"] == "weekly"

        # Verify the SQL includes a report_type condition
        call_args = mock_pool.fetch.call_args
        sql = call_args[0][0]
        assert "report_type" in sql
        # "weekly" should be among the positional params
        assert "weekly" in call_args[0]

    @pytest.mark.asyncio
    async def test_filter_by_date_range(self) -> None:
        """Filtering by start_date and end_date passes dates to the query."""
        mock_pool = AsyncMock()
        mock_pool.fetch = AsyncMock(return_value=[])

        with patch(_POOL_PATCH, mock_pool):
            async with httpx.AsyncClient(
                transport=httpx.ASGITransport(app=app), base_url="http://test"
            ) as client:
                resp = await client.get(
                    "/api/reports",
                    params={"start_date": "2025-01-01", "end_date": "2025-01-31"},
                )

        assert resp.status_code == 200
        call_args = mock_pool.fetch.call_args
        sql = call_args[0][0]
        assert "period_start" in sql
        assert "period_end" in sql
        # Date strings should be among the positional params
        assert "2025-01-01" in call_args[0]
        assert "2025-01-31" in call_args[0]

    @pytest.mark.asyncio
    async def test_invalid_report_type_returns_400(self) -> None:
        """An invalid report_type value returns HTTP 400."""
        mock_pool = AsyncMock()

        with patch(_POOL_PATCH, mock_pool):
            async with httpx.AsyncClient(
                transport=httpx.ASGITransport(app=app), base_url="http://test"
            ) as client:
                resp = await client.get(
                    "/api/reports", params={"report_type": "monthly"}
                )

        assert resp.status_code == 400
        assert "daily" in resp.json()["detail"].lower() or "weekly" in resp.json()["detail"].lower()
        # pool.fetch should NOT have been called
        mock_pool.fetch.assert_not_awaited()

    @pytest.mark.asyncio
    async def test_invalid_date_format_returns_400(self) -> None:
        """A malformed start_date returns HTTP 400."""
        mock_pool = AsyncMock()

        with patch(_POOL_PATCH, mock_pool):
            async with httpx.AsyncClient(
                transport=httpx.ASGITransport(app=app), base_url="http://test"
            ) as client:
                resp = await client.get(
                    "/api/reports", params={"start_date": "not-a-date"}
                )

        assert resp.status_code == 400
        assert "YYYY-MM-DD" in resp.json()["detail"]
        mock_pool.fetch.assert_not_awaited()


# ═══════════════════════════════════════════════════════════════════════
# 2. GET /api/reports/{report_id} — detail endpoint
# Requirements validated: 5.4, 5.5
# ═══════════════════════════════════════════════════════════════════════


class TestGetReport:
    """Tests for GET /api/reports/{report_id}."""

    @pytest.mark.asyncio
    async def test_valid_id_returns_full_report(self) -> None:
        """A valid report_id returns the full report including report_data."""
        record = _make_detail_record()
        mock_pool = AsyncMock()
        mock_pool.fetchrow = AsyncMock(return_value=record)

        report_id = str(record["id"])

        with patch(_POOL_PATCH, mock_pool):
            async with httpx.AsyncClient(
                transport=httpx.ASGITransport(app=app), base_url="http://test"
            ) as client:
                resp = await client.get(f"/api/reports/{report_id}")

        assert resp.status_code == 200
        data = resp.json()
        assert data["id"] == report_id
        assert data["report_type"] == "daily"
        assert data["period_start"] == "2025-01-15"
        assert data["period_end"] == "2025-01-15"
        assert data["validation_status"] == "passed"
        assert "generated_at" in data
        assert "created_at" in data
        # report_data is included as a dict
        assert isinstance(data["report_data"], dict)
        assert data["report_data"]["pnl"]["realized_pnl"] == 125.50
        assert data["report_data"]["executive_summary"] == "Test summary"

    @pytest.mark.asyncio
    async def test_nonexistent_id_returns_404(self) -> None:
        """A non-existent report_id returns HTTP 404."""
        mock_pool = AsyncMock()
        mock_pool.fetchrow = AsyncMock(return_value=None)

        fake_id = str(uuid.uuid4())

        with patch(_POOL_PATCH, mock_pool):
            async with httpx.AsyncClient(
                transport=httpx.ASGITransport(app=app), base_url="http://test"
            ) as client:
                resp = await client.get(f"/api/reports/{fake_id}")

        assert resp.status_code == 404
        assert "not found" in resp.json()["detail"].lower()
