from __future__ import annotations

import os
from unittest.mock import AsyncMock, MagicMock, patch

import pandas as pd
import pytest

os.environ.setdefault("BRAIN_BOT_URL", "http://localhost:9001")

from data.fetcher import KlinesFetcher


def make_mock_klines(n: int = 5) -> list:
    """بيانات klines مزيفة بتنسيق Binance."""
    return [
        [
            1_700_000_000_000 + i * 60_000,  # open_time ms
            str(60000 + i),  # open
            str(60100 + i),  # high
            str(59900 + i),  # low
            str(60050 + i),  # close
            str(1.5),        # volume
            1_700_000_060_000 + i * 60_000,  # close_time
            "90000", "100", "0.75", "45000", "0",
        ]
        for i in range(n)
    ]


@pytest.mark.asyncio
async def test_fetch_returns_dataframe():
    fetcher = KlinesFetcher()
    mock_resp = MagicMock()
    mock_resp.status_code = 200
    mock_resp.json.return_value = make_mock_klines(10)
    mock_resp.raise_for_status = MagicMock()

    mock_client = AsyncMock()
    mock_client.get = AsyncMock(return_value=mock_resp)
    fetcher._client = mock_client

    df = await fetcher.fetch("BTCUSDT", "5m")
    assert isinstance(df, pd.DataFrame)
    assert len(df) == 10


@pytest.mark.asyncio
async def test_fetch_close_is_float():
    fetcher = KlinesFetcher()
    mock_resp = MagicMock()
    mock_resp.status_code = 200
    mock_resp.json.return_value = make_mock_klines(5)
    mock_resp.raise_for_status = MagicMock()

    mock_client = AsyncMock()
    mock_client.get = AsyncMock(return_value=mock_resp)
    fetcher._client = mock_client

    df = await fetcher.fetch("BTCUSDT", "1h")
    assert df["close"].dtype == float


@pytest.mark.asyncio
async def test_fetch_sorted_ascending():
    fetcher = KlinesFetcher()
    raw = make_mock_klines(10)
    raw_reversed = list(reversed(raw))  # ترتيب عكسي

    mock_resp = MagicMock()
    mock_resp.status_code = 200
    mock_resp.json.return_value = raw_reversed
    mock_resp.raise_for_status = MagicMock()

    mock_client = AsyncMock()
    mock_client.get = AsyncMock(return_value=mock_resp)
    fetcher._client = mock_client

    df = await fetcher.fetch("ETHUSDT", "5m")
    times = df["open_time"].tolist()
    assert times == sorted(times)


@pytest.mark.asyncio
async def test_invalid_timeframe_raises():
    fetcher = KlinesFetcher()
    fetcher._client = AsyncMock()
    with pytest.raises(ValueError, match="إطار زمني غير صالح"):
        await fetcher.fetch("BTCUSDT", "99x")
