"""Tests for the ingestion service — honest reporting of empty provider fetches."""

from __future__ import annotations

from datetime import date, timedelta

import pytest

from app.models.ohlcv import OHLCVRecord
from app.models.settings import IngestionProgress
from app.models.ticker import Ticker
from app.providers.protocol import OHLCVData
from app.services import ingestion_service as svc
from tests.conftest import MockMarketDataProvider, _test_session_factory  # type: ignore


@pytest.fixture
async def session():
    async with _test_session_factory() as s:
        yield s


async def _add_ticker(session, symbol: str) -> Ticker:
    ticker = Ticker(symbol=symbol)
    session.add(ticker)
    await session.commit()
    await session.refresh(ticker)
    return ticker


def _bars(symbol: str, n: int) -> list[OHLCVData]:
    today = date.today()
    return [
        OHLCVData(ticker=symbol, date=today - timedelta(days=i),
                  open=100.0, high=101.0, low=99.0, close=100.0, volume=1000)
        for i in range(n)
    ]


async def test_empty_fetch_on_new_ticker_reports_no_data(session):
    # A non-US symbol (e.g. RHM/Rheinmetall) Alpaca doesn't cover → empty bars.
    # This must NOT report success; it surfaces as no_data.
    await _add_ticker(session, "RHM")
    result = await svc.fetch_and_ingest(session, MockMarketDataProvider(ohlcv_data=[]), "RHM")

    assert result.status == "no_data"
    assert result.records_ingested == 0
    assert "provider" in (result.message or "").lower()


async def test_happy_path_ingests_bars(session):
    await _add_ticker(session, "AAA")
    result = await svc.fetch_and_ingest(session, MockMarketDataProvider(ohlcv_data=_bars("AAA", 3)), "AAA")

    assert result.status == "complete"
    assert result.records_ingested == 3


async def test_incremental_fetch_overlaps_latest_session_and_updates_partial_bar(session):
    """Once today exists, a live refresh must fetch and overwrite it again."""
    ticker = await _add_ticker(session, "LIVE")
    today = date.today()
    session.add_all([
        OHLCVRecord(
            ticker_id=ticker.id,
            date=today - timedelta(days=i),
            open=100.0,
            high=101.0,
            low=99.0,
            close=100.0,
            volume=1000,
        )
        for i in range(200)
    ])
    session.add(IngestionProgress(ticker_id=ticker.id, last_ingested_date=today))
    await session.commit()

    provider = MockMarketDataProvider(ohlcv_data=[
        OHLCVData(
            ticker="LIVE",
            date=today,
            open=100.0,
            high=124.0,
            low=99.0,
            close=123.0,
            volume=2000,
        )
    ])
    result = await svc.fetch_and_ingest(session, provider, "LIVE")

    assert provider.calls == [{
        "ticker": "LIVE",
        "start_date": today,
        "end_date": today,
    }]
    assert result.status == "complete"
    assert result.records_ingested == 1
    records = await svc.price_service.query_ohlcv(session, "LIVE", today, today)
    assert records[0].close == 123.0
    assert records[0].volume == 2000


async def test_empty_fetch_with_existing_history_is_up_to_date(session):
    # Covered ticker, just no new bars in the window → complete, not no_data.
    await _add_ticker(session, "BBB")
    await svc.fetch_and_ingest(session, MockMarketDataProvider(ohlcv_data=_bars("BBB", 2)), "BBB")

    result = await svc.fetch_and_ingest(session, MockMarketDataProvider(ohlcv_data=[]), "BBB")

    assert result.status == "complete"
    assert result.records_ingested == 0


async def test_empty_fetch_with_stale_history_reports_stale(session):
    # Provider returns nothing but last stored bar is weeks old — classic
    # rename/delist case (e.g. SATS after it became ECHO). Must not look like success.
    await _add_ticker(session, "SATS")
    today = date.today()
    old = [
        OHLCVData(
            ticker="SATS",
            date=today - timedelta(days=20 + i),
            open=100.0,
            high=101.0,
            low=99.0,
            close=100.0,
            volume=1000,
        )
        for i in range(3)
    ]
    await svc.fetch_and_ingest(session, MockMarketDataProvider(ohlcv_data=old), "SATS")

    # Incremental overlap means Alpaca can keep returning the final historical
    # bar. That is still stale: the latest session did not advance.
    result = await svc.fetch_and_ingest(
        session,
        MockMarketDataProvider(ohlcv_data=[old[-1]]),
        "SATS",
    )

    assert result.status == "stale"
    assert result.records_ingested == 1
    assert result.last_date is not None
    assert "renamed" in (result.message or "").lower() or "halted" in (result.message or "").lower()


async def test_ingest_can_skip_sr_refresh_when_scanner_follows(session, monkeypatch):
    await _add_ticker(session, "SCAN")
    calls: list[str] = []

    async def fake_refresh(db, symbol):
        calls.append(symbol)

    monkeypatch.setattr(svc, "_refresh_structural_sr", fake_refresh)
    result = await svc.fetch_and_ingest(
        session,
        MockMarketDataProvider(ohlcv_data=_bars("SCAN", 3)),
        "SCAN",
        refresh_sr=False,
    )

    assert result.status == "complete"
    assert calls == []
