"""Tests for the read-only external data API (`/api/v1`) used by Power BI.

Drives the real FastAPI app via TestClient against an in-memory database, and
swaps the configured API key in by replacing the module-level `settings` object
(the real one is a frozen dataclass).
"""
from __future__ import annotations

from dataclasses import replace
from datetime import date

import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool

from app.api import public_v1
from app.core.config import settings
from app.db.session import Base, get_db
from app.main import app
from app.models.throughput import ProductionThroughput

API_KEY = "test-powerbi-key"
TENANT = "hunter-premium-produce"


def _seed_entry(db, *, tenant_id: str, product: str, quantity: float, when: date) -> None:
    db.add(
        ProductionThroughput(
            tenant_id=tenant_id,
            production_date=when,
            product_name_snapshot=product,
            bag_size=20,
            quantity=quantity,
            quantity_type="bags",
            calculated_kg=quantity * 20,
            created_by="test",
        )
    )


@pytest.fixture()
def db_factory():
    engine = create_engine(
        "sqlite:///:memory:",
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    Base.metadata.create_all(bind=engine)
    TestingSession = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False)

    def override_get_db():
        db = TestingSession()
        try:
            yield db
        finally:
            db.close()

    app.dependency_overrides[get_db] = override_get_db
    yield TestingSession
    app.dependency_overrides.clear()


def _make_client(monkeypatch, *, api_key: str) -> TestClient:
    # Replace the whole settings object the router reads (frozen dataclass).
    monkeypatch.setattr(public_v1, "settings", replace(settings, powerbi_api_key=api_key, powerbi_tenant_id=TENANT))
    return TestClient(app)


def test_disabled_when_key_unset(monkeypatch, db_factory):
    client = _make_client(monkeypatch, api_key="")
    response = client.get("/api/v1/throughput", headers={"X-API-Key": "anything"})
    assert response.status_code == 503


def test_rejects_missing_and_wrong_key(monkeypatch, db_factory):
    client = _make_client(monkeypatch, api_key=API_KEY)
    assert client.get("/api/v1/throughput").status_code == 401
    assert client.get("/api/v1/throughput", headers={"X-API-Key": "nope"}).status_code == 401


def test_returns_entries_for_tenant(monkeypatch, db_factory):
    db = db_factory()
    _seed_entry(db, tenant_id=TENANT, product="Maize", quantity=10, when=date(2026, 1, 5))
    _seed_entry(db, tenant_id=TENANT, product="Barley", quantity=5, when=date(2026, 1, 6))
    # An entry in another tenant must never leak through.
    _seed_entry(db, tenant_id="someone-else", product="Secret", quantity=99, when=date(2026, 1, 7))
    db.commit()
    db.close()

    client = _make_client(monkeypatch, api_key=API_KEY)

    # Header auth.
    response = client.get("/api/v1/throughput", headers={"X-API-Key": API_KEY})
    assert response.status_code == 200
    rows = response.json()
    names = [row["product_name_snapshot"] for row in rows]
    assert names == ["Maize", "Barley"]  # oldest first, other tenant excluded
    assert rows[0]["calculated_kg"] == 200.0

    # Query-string auth works too (Power BI Web connector convenience).
    assert client.get(f"/api/v1/throughput?api_key={API_KEY}").status_code == 200


def test_date_filter(monkeypatch, db_factory):
    db = db_factory()
    _seed_entry(db, tenant_id=TENANT, product="Old", quantity=1, when=date(2026, 1, 1))
    _seed_entry(db, tenant_id=TENANT, product="New", quantity=1, when=date(2026, 2, 1))
    db.commit()
    db.close()

    client = _make_client(monkeypatch, api_key=API_KEY)
    response = client.get("/api/v1/throughput?date_from=2026-01-15", headers={"X-API-Key": API_KEY})
    assert [row["product_name_snapshot"] for row in response.json()] == ["New"]
