"""The tests for OctoPrint number module."""

from datetime import UTC, datetime
from typing import Any
from unittest.mock import patch

import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.number import (
    ATTR_VALUE,
    DOMAIN as NUMBER_DOMAIN,
    SERVICE_SET_VALUE,
)
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er

from tests.common import MockConfigEntry, snapshot_platform


@pytest.fixture
def platform() -> Platform:
    """Fixture to specify platform."""
    return Platform.NUMBER


@pytest.fixture
def job() -> dict[str, Any]:
    """Job fixture."""
    return __standard_job()


@pytest.mark.parametrize(
    "printer",
    [
        {
            "state": {
                "flags": {"printing": True},
                "text": "Operational",
            },
            "temperature": {
                "tool0": {"actual": 18.83136, "target": 37.83136},
                "tool1": {"actual": 21.0, "target": 31.0},
                "bed": {"actual": 25.5, "target": 60.0},
            },
        },
    ],
)
@pytest.mark.freeze_time(datetime(2020, 2, 20, 9, 10, 13, 543, tzinfo=UTC))
async def test_numbers(
    hass: HomeAssistant,
    entity_registry: er.EntityRegistry,
    snapshot: SnapshotAssertion,
    init_integration: MockConfigEntry,
) -> None:
    """Test the underlying number entities."""
    await snapshot_platform(hass, entity_registry, snapshot, init_integration.entry_id)


@pytest.mark.parametrize(
    "printer",
    [
        {
            "state": {
                "flags": {"printing": True, "paused": False},
                "text": "Operational",
            },
            "temperature": {
                "tool0": {"actual": 18.83136, "target": None},
                "bed": {"actual": 25.5, "target": None},
            },
        },
    ],
)
@pytest.mark.freeze_time(datetime(2020, 2, 20, 9, 10, 0))
@pytest.mark.usefixtures("init_integration")
async def test_numbers_no_target_temp(
    hass: HomeAssistant,
    entity_registry: er.EntityRegistry,
) -> None:
    """Test the number entities when target temperature is None."""
    state = hass.states.get("number.octoprint_extruder_temperature")
    assert state is not None
    assert state.state == STATE_UNKNOWN
    assert state.name == "OctoPrint Extruder temperature"
    entry = entity_registry.async_get("number.octoprint_extruder_temperature")
    assert entry.unique_id == "uuid_tool0_temperature"

    state = hass.states.get("number.octoprint_bed_temperature")
    assert state is not None
    assert state.state == STATE_UNKNOWN
    assert state.name == "OctoPrint Bed temperature"
    entry = entity_registry.async_get("number.octoprint_bed_temperature")
    assert entry.unique_id == "uuid_bed_temperature"


@pytest.mark.parametrize(
    "printer",
    [
        {
            "state": {
                "flags": {"printing": False},
                "text": "Operational",
            },
            "temperature": {"tool0": {"actual": 18.83136, "target": 25.0}},
        },
    ],
)
@pytest.mark.freeze_time(datetime(2020, 2, 20, 9, 10, 0))
@pytest.mark.usefixtures("init_integration")
async def test_set_tool_temp(
    hass: HomeAssistant,
) -> None:
    """Test setting tool temperature via number entity."""
    with patch(
        "pyoctoprintapi.OctoprintClient.set_tool_temperature"
    ) as mock_set_tool_temp:
        entity_component = hass.data[NUMBER_DOMAIN]

        entity = entity_component.get_entity("number.octoprint_extruder_temperature")
        assert entity is not None

        await hass.services.async_call(
            NUMBER_DOMAIN,
            SERVICE_SET_VALUE,
            {ATTR_ENTITY_ID: entity.entity_id, ATTR_VALUE: 200.4},
            blocking=True,
        )
        assert len(mock_set_tool_temp.mock_calls) == 1
        # Verify that we pass integer, expected by the pyoctoprintapi
        mock_set_tool_temp.assert_called_with("tool0", 200)


@pytest.mark.parametrize(
    "printer",
    [
        {
            "state": {
                "flags": {"printing": False},
                "text": "Operational",
            },
            "temperature": {"bed": {"actual": 20.0, "target": 50.0}},
        },
    ],
)
@pytest.mark.freeze_time(datetime(2020, 2, 20, 9, 10, 0))
@pytest.mark.usefixtures("init_integration")
async def test_set_bed_temp(
    hass: HomeAssistant,
) -> None:
    """Test setting bed temperature via number entity."""
    with patch(
        "pyoctoprintapi.OctoprintClient.set_bed_temperature"
    ) as mock_set_bed_temp:
        entity_component = hass.data[NUMBER_DOMAIN]
        entity = entity_component.get_entity("number.octoprint_bed_temperature")
        assert entity is not None

        await hass.services.async_call(
            NUMBER_DOMAIN,
            SERVICE_SET_VALUE,
            {ATTR_ENTITY_ID: entity.entity_id, ATTR_VALUE: 80.6},
            blocking=True,
        )

        assert len(mock_set_bed_temp.mock_calls) == 1
        # Verify that we pass integer, expected by pyoctoprintapi,
        # and that it's rounded down
        mock_set_bed_temp.assert_called_with(80)


@pytest.mark.parametrize(
    "printer",
    [
        {
            "state": {
                "flags": {"printing": False},
                "text": "Operational",
            },
            "temperature": {
                "tool0": {"actual": 20.0, "target": 30.0},
                "tool1": {"actual": 21.0, "target": 31.0},
            },
        },
    ],
)
@pytest.mark.freeze_time(datetime(2020, 2, 20, 9, 10, 0))
@pytest.mark.usefixtures("init_integration")
async def test_set_tool_n_temp(
    hass: HomeAssistant,
) -> None:
    """Test setting tool temperature when multiple tools present."""
    with patch(
        "pyoctoprintapi.OctoprintClient.set_tool_temperature"
    ) as mock_set_tool_temp:
        entity_component = hass.data[NUMBER_DOMAIN]

        entity = entity_component.get_entity("number.octoprint_extruder_1_temperature")
        assert entity is not None

        await hass.services.async_call(
            NUMBER_DOMAIN,
            SERVICE_SET_VALUE,
            {ATTR_ENTITY_ID: entity.entity_id, ATTR_VALUE: 41.0},
            blocking=True,
        )
        assert len(mock_set_tool_temp.mock_calls) == 1
        # Verify that we pass integer, expected by the pyoctoprintapi
        mock_set_tool_temp.assert_called_with("tool1", 41)


@pytest.mark.parametrize("printer", [None])
@pytest.mark.freeze_time(datetime(2020, 2, 20, 9, 10, 0))
@pytest.mark.usefixtures("init_integration")
async def test_numbers_printer_disconnected(
    hass: HomeAssistant,
) -> None:
    """Test number entities when printer is disconnected."""
    # When printer is disconnected, no number entities should be created
    state = hass.states.get("number.octoprint_tool0_temperature")
    assert state is None

    state = hass.states.get("number.octoprint_bed_temperature")
    assert state is None


def __standard_job():
    return {
        "job": {
            "averagePrintTime": 6500,
            "estimatedPrintTime": 6000,
            "filament": {"tool0": {"length": 3000, "volume": 7}},
            "file": {
                "date": 1577836800,
                "display": "Test File Name",
                "name": "Test_File_Name.gcode",
                "origin": "local",
                "path": "Folder1/Folder2/Test_File_Name.gcode",
                "size": 123456789,
            },
            "lastPrintTime": 12345.678,
            "user": "testUser",
        },
        "progress": {"completion": 50, "printTime": 600, "printTimeLeft": 6000},
        "state": "Printing",
    }
