from datetime import UTC, datetime
from zoneinfo import ZoneInfo

import httpx
import pytest

from dalysdk.querying import prepare_query_weather_data
from tests.test_resources import _make_client


class DataFrame:
    __module__ = "pandas.core.frame"

    def __init__(self, records: list[dict]):
        self._records = records

    def to_dict(self, orient: str) -> list[dict]:
        assert orient == "records"
        return self._records


def _base_input_with_location() -> dict:
    return {
        "location": {
            "latitude": 33.4484,
            "longitude": -112.074,
            "timeZone": -7,
            "timeZoneName": "America/Phoenix",
        },
        "blocks": [{"name": "Block A"}],
    }


def _weather_records() -> list[dict]:
    return [
        {"timestamp": "2024-01-01T00:00:00Z", "ghi": 100.0, "dhi": 40.0, "temp": 20.0, "wind_speed": 3.0},
        {"timestamp": "2024-01-01T01:00:00Z", "ghi": 110.0, "dhi": 45.0, "temp": 21.0, "wind_speed": 3.5},
        {"timestamp": "2024-01-01T02:00:00Z", "ghi": 120.0, "dhi": 50.0, "temp": 22.0, "wind_speed": 4.0},
    ]


def test_prepare_query_weather_data_accepts_dataframe_like_input():
    weather = prepare_query_weather_data(DataFrame(_weather_records()), time_step_format="utc")

    assert len(weather["date"]) == 3
    assert weather["ghi"] == [100.0, 110.0, 120.0]
    assert weather["temperature"] == [20.0, 21.0, 22.0]
    assert weather["windSpeed"] == [3.0, 3.5, 4.0]


def test_build_query_payload_filters_utc_weather_window_without_mutating_base_input():
    client = _make_client({})
    base_input = {"locationId": 9, "blocks": [{"name": "Block A"}]}

    payload = client.energy_models.build_query_payload(
        base_input,
        _weather_records(),
        start_date="2024-01-01T01:00:00Z",
        end_date="2024-01-01T02:00:00Z",
        time_step_format="utc",
    )

    assert "weatherData" not in base_input
    assert payload["locationId"] == 9
    assert len(payload["weatherData"]["date"]) == 2
    assert payload["weatherData"]["ghi"] == [110.0, 120.0]
    client.close()


def test_build_query_payload_converts_local_wall_clock_time_using_location_timezone():
    client = _make_client({})
    payload = client.energy_models.build_query_payload(
        _base_input_with_location(),
        [
            {
                "date": "2024-01-01T00:00:00",
                "ghi": 100.0,
                "dhi": 40.0,
                "temperature": 20.0,
                "windSpeed": 3.0,
            }
        ],
        time_step_format="local",
    )

    expected = int(datetime(2024, 1, 1, 0, 0, tzinfo=ZoneInfo("America/Phoenix")).astimezone(UTC).timestamp() * 1000)
    assert payload["weatherData"]["date"] == [expected]
    client.close()


def test_build_query_payload_converts_offset_timestamps_using_explicit_offset():
    client = _make_client({})
    payload = client.energy_models.build_query_payload(
        {"projectId": 7, "blocks": [{"name": "Block A"}]},
        [
            {
                "date": "2024-01-01T00:00:00",
                "ghi": 100.0,
                "dhi": 40.0,
                "temperature": 20.0,
                "windSpeed": 3.0,
            }
        ],
        time_step_format="offset",
        timezone_offset=-5.0,
    )

    expected = int(datetime(2024, 1, 1, 5, 0, tzinfo=UTC).timestamp() * 1000)
    assert payload["weatherData"]["date"] == [expected]
    client.close()


def test_query_posts_async_with_built_payload_and_accepted_response():
    requests: list[httpx.Request] = []
    client = _make_client(
        {
            "POST /energymodels": httpx.Response(
                202,
                json={"result": "success", "taskDetails": {"taskId": 5}},
            ),
        },
        seen_requests=requests,
    )

    result = client.energy_models.query(
        {"locationId": 9, "blocks": [{"name": "Block A"}]},
        _weather_records(),
        start_date="2024-01-01T01:00:00Z",
        end_date="2024-01-01T01:00:00Z",
        async_mode=True,
    )

    assert result["taskDetails"]["taskId"] == 5
    posted = requests[-1].read().decode("utf-8")
    assert '"weatherData"' in posted
    assert posted.count('"ghi"') == 1
    client.close()


def test_build_query_payload_rejects_empty_filtered_window():
    client = _make_client({})

    with pytest.raises(ValueError, match="empty after applying the requested query window"):
        client.energy_models.build_query_payload(
            {"locationId": 9, "blocks": [{"name": "Block A"}]},
            _weather_records(),
            start_date="2024-01-02T00:00:00Z",
            end_date="2024-01-02T01:00:00Z",
        )

    client.close()


def test_build_query_payload_requires_timezone_name_for_local_mode():
    client = _make_client({})

    with pytest.raises(ValueError, match="requires timezone_name or location.timeZoneName"):
        client.energy_models.build_query_payload(
            {"locationId": 9, "blocks": [{"name": "Block A"}]},
            [
                {
                    "date": "2024-01-01T00:00:00",
                    "ghi": 100.0,
                    "dhi": 40.0,
                    "temperature": 20.0,
                    "windSpeed": 3.0,
                }
            ],
            time_step_format="local",
        )

    client.close()
