"""Tests for the SolaxClient."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock

import aiohttp
import pytest

from aiosolax import (
    SolaxAllZeroError,
    SolaxAuthenticationError,
    SolaxClient,
    SolaxConnectionError,
    SolaxError,
    SolaxResponseError,
    SolaxTimeoutError,
)

from .conftest import mock_response


async def test_get_data_success(mock_session, x1_hybrid_gen4_fixture):
    """Test successful data fetch and parse."""
    mock_session.post = MagicMock(
        return_value=mock_response(json_data=x1_hybrid_gen4_fixture)
    )

    client = SolaxClient("192.168.1.106", "SWABCDEF1234", session=mock_session)
    info, data = await client.get_data()

    assert info.serial_number == "H4372AI4633110"
    assert info.inverter_type == 15
    assert info.rated_power == 3.7
    assert data.grid_voltage == 241.4
    assert data.pv1_power == 2861
    assert data.battery_soc == 45

    # Verify POST was called correctly
    mock_session.post.assert_called_once()
    call_args = mock_session.post.call_args
    assert "192.168.1.106" in call_args[0][0]
    assert "optType=ReadRealTimeData" in call_args[1]["data"]
    assert "pwd=SWABCDEF1234" in call_args[1]["data"]


async def test_basic_auth_sent_when_username_provided(
    mock_session, x1_hybrid_gen4_fixture
):
    """Test that Basic Auth is sent when username is provided."""
    mock_session.post = MagicMock(
        return_value=mock_response(json_data=x1_hybrid_gen4_fixture)
    )

    client = SolaxClient(
        "192.168.1.106", "password", username="admin", session=mock_session
    )
    await client.get_data()

    call_kwargs = mock_session.post.call_args[1]
    assert "auth" in call_kwargs
    assert isinstance(call_kwargs["auth"], aiohttp.BasicAuth)
    assert call_kwargs["auth"].login == "admin"


async def test_no_basic_auth_when_no_username(mock_session, x1_hybrid_gen4_fixture):
    """Test that Basic Auth is NOT sent when no username is provided."""
    mock_session.post = MagicMock(
        return_value=mock_response(json_data=x1_hybrid_gen4_fixture)
    )

    client = SolaxClient("192.168.1.106", "password", session=mock_session)
    await client.get_data()

    call_kwargs = mock_session.post.call_args[1]
    assert "auth" not in call_kwargs


async def test_all_zero_data_raises(mock_session):
    """Test that an all-zero Data array raises SolaxAllZeroError."""
    response = {
        "sn": "TEST",
        "ver": "1.0",
        "type": 15,
        "Data": [0] * 40,
        "Information": [3.7, "V3.0", "SN123", 8],
    }
    mock_session.post = MagicMock(
        return_value=mock_response(json_data=response)
    )

    client = SolaxClient("192.168.1.106", "password", session=mock_session)
    with pytest.raises(SolaxAllZeroError):
        await client.get_data()


async def test_invalid_response_missing_data(mock_session):
    """Test that a response missing 'Data' raises SolaxResponseError."""
    mock_session.post = MagicMock(
        return_value=mock_response(json_data={"sn": "TEST", "ver": "1.0"})
    )

    client = SolaxClient("192.168.1.106", "password", session=mock_session)
    with pytest.raises(SolaxResponseError, match="missing required fields"):
        await client.get_data()


async def test_unsupported_inverter_type(mock_session):
    """Test that an unsupported inverter type raises SolaxResponseError."""
    response = {
        "sn": "TEST",
        "ver": "1.0",
        "type": 999,
        "Data": [1, 2, 3],
        "Information": [3.7, "V3.0", "SN123"],
    }
    mock_session.post = MagicMock(
        return_value=mock_response(json_data=response)
    )

    client = SolaxClient("192.168.1.106", "password", session=mock_session)
    with pytest.raises(SolaxResponseError, match="Unsupported inverter type"):
        await client.get_data()


async def test_http_401_raises_auth_error(mock_session):
    """Test that HTTP 401 raises SolaxAuthenticationError."""
    mock_session.post = MagicMock(
        return_value=mock_response(status=401)
    )

    client = SolaxClient("192.168.1.106", "password", session=mock_session)
    with pytest.raises(SolaxAuthenticationError):
        await client.get_data()


async def test_timeout_raises(mock_session):
    """Test that a timeout raises SolaxTimeoutError."""
    mock_session.post = MagicMock(side_effect=TimeoutError("timed out"))

    client = SolaxClient("192.168.1.106", "password", session=mock_session)
    with pytest.raises(SolaxTimeoutError):
        await client.get_data()


async def test_connection_error_raises(mock_session):
    """Test that a connection error raises SolaxConnectionError."""
    mock_session.post = MagicMock(
        side_effect=aiohttp.ClientError("connection refused")
    )

    client = SolaxClient("192.168.1.106", "password", session=mock_session)
    with pytest.raises(SolaxConnectionError):
        await client.get_data()


async def test_no_session_raises(mock_session):
    """Test that calling get_data without a session raises SolaxError."""
    client = SolaxClient("192.168.1.106", "password")
    with pytest.raises(SolaxError, match="Session not initialized"):
        await client.get_data()


async def test_context_manager_creates_and_closes_session():
    """Test that the context manager creates and closes an owned session."""
    client = SolaxClient("192.168.1.106", "password")
    assert client._session is None

    async with client:
        assert client._session is not None
        session = client._session

    assert client._session is None


async def test_context_manager_does_not_close_injected_session(mock_session):
    """Test that the context manager does NOT close an injected session."""
    client = SolaxClient("192.168.1.106", "password", session=mock_session)

    async with client:
        assert client._session is mock_session

    # Session should still be set (not closed by us)
    assert client._session is mock_session


async def test_custom_port(mock_session, x1_hybrid_gen4_fixture):
    """Test that a custom port is used in the URL."""
    mock_session.post = MagicMock(
        return_value=mock_response(json_data=x1_hybrid_gen4_fixture)
    )

    client = SolaxClient(
        "192.168.1.106", "password", port=8080, session=mock_session
    )
    await client.get_data()

    call_args = mock_session.post.call_args
    assert ":8080/" in call_args[0][0]
