from datetime import datetime, timezone

import httpx
import pytest

from app.services.metals.bullionvault_service import BullionVaultService


def _csv_response(rows: list[str]) -> str:
	header = (
		'"Date","High (kg)","Low (kg)","Close (kg)",,"High (troy oz)","Low (troy oz)","Close (troy oz)",\n'
	)
	return header + "".join(f"{row}\n" for row in rows)


def _mock_client(handler):
	transport = httpx.MockTransport(handler)
	return httpx.AsyncClient(transport=transport)


@pytest.mark.asyncio
async def test_get_current_gold_price_parses_csv() -> None:
	csv = _csv_response(
		[
			'"12:00:00 23-Nov-2025",130702.99,130600.11,130650.10,,4065.32,4040.32,4050.55,',
			'"11:50:00 23-Nov-2025",130600.99,130500.11,130550.10,,4055.32,4030.32,4040.55,',
		]
	)

	async def handler(request: httpx.Request) -> httpx.Response:
		return httpx.Response(200, text=csv)

	async with _mock_client(handler) as client:
		service = BullionVaultService(client=client, base_url="https://chart-data.bullionvault.com")
		result = await service.get_current_gold_price("usd")

	assert result["currency"] == "USD"
	assert result["price"] == pytest.approx(4050.55, rel=0.001)
	assert result["price_kg"] == pytest.approx(130650.10, rel=0.001)
	assert result["data_points"] == 2
	assert datetime.fromisoformat(result["timestamp"]).tzinfo == timezone.utc


@pytest.mark.asyncio
async def test_get_current_gold_price_raises_on_empty_data() -> None:
	async def handler(_: httpx.Request) -> httpx.Response:
		return httpx.Response(200, text='"Date",\n')

	async with _mock_client(handler) as client:
		service = BullionVaultService(client=client)

		with pytest.raises(ValueError, match="No price data available"):
			await service.get_current_gold_price()


@pytest.mark.asyncio
async def test_get_gold_history_applies_limit() -> None:
	csv = _csv_response(
		[
			'"12:00:00 23-Nov-2025",130702.99,130600.11,130650.10,,4065.32,4040.32,4050.55,',
			'"11:50:00 23-Nov-2025",130600.99,130500.11,130550.10,,4055.32,4030.32,4040.55,',
			'"11:40:00 23-Nov-2025",130500.99,130400.11,130450.10,,4045.32,4020.32,4030.55,',
		]
	)

	async def handler(_: httpx.Request) -> httpx.Response:
		return httpx.Response(200, text=csv)

	async with _mock_client(handler) as client:
		service = BullionVaultService(client=client)
		history = await service.get_gold_history(limit=2)

	assert len(history) == 2
	assert history[0]["close"] == pytest.approx(4050.55, rel=0.001)
	assert history[1]["close"] == pytest.approx(4040.55, rel=0.001)


@pytest.mark.asyncio
async def test_get_multi_currency_prices_skips_failures() -> None:
	csv = _csv_response(
		[
			'"12:00:00 23-Nov-2025",130702.99,130600.11,130650.10,,4065.32,4040.32,4050.55,',
		]
	)

	retry_counts: dict[str, int] = {}

	async def handler(request: httpx.Request) -> httpx.Response:
		currency = request.url.path.split("/")[4]
		retry_counts[currency] = retry_counts.get(currency, 0) + 1

		if currency == "GBP":
			return httpx.Response(500, text="error")

		return httpx.Response(200, text=csv)

	async with _mock_client(handler) as client:
		service = BullionVaultService(client=client, max_retries=1)
		prices = await service.get_multi_currency_prices()

	# Only currencies served by successful responses should be included
	assert "USD" in prices
	assert "EUR" in prices
	assert "GBP" not in prices
	assert retry_counts["GBP"] == 1
