"""Tests for Minio Hass related code."""
import asyncio
import json
from unittest.mock import MagicMock, call, patch

import pytest

from homeassistant.components.minio import (
    CONF_ACCESS_KEY,
    CONF_HOST,
    CONF_LISTEN,
    CONF_LISTEN_BUCKET,
    CONF_PORT,
    CONF_SECRET_KEY,
    CONF_SECURE,
    DOMAIN,
    QueueListener,
)
from homeassistant.core import callback
from homeassistant.setup import async_setup_component

from tests.components.minio.common import TEST_EVENT


@pytest.fixture(name="minio_client")
def minio_client_fixture():
    """Patch Minio client."""
    with patch("homeassistant.components.minio.minio_helper.Minio") as minio_mock:
        minio_client_mock = minio_mock.return_value

        yield minio_client_mock


@pytest.fixture(name="minio_client_event")
def minio_client_event_fixture():
    """Patch helper function for minio notification stream."""
    with patch("homeassistant.components.minio.minio_helper.Minio") as minio_mock:
        minio_client_mock = minio_mock.return_value

        response_mock = MagicMock()
        stream_mock = MagicMock()

        stream_mock.__next__.side_effect = [
            "",
            "",
            bytearray(json.dumps(TEST_EVENT), "utf-8"),
        ]

        response_mock.stream.return_value = stream_mock
        minio_client_mock._url_open.return_value = response_mock

        yield minio_client_mock


async def test_minio_services(hass, caplog, minio_client):
    """Test Minio services."""
    hass.config.allowlist_external_dirs = {"/test"}

    await async_setup_component(
        hass,
        DOMAIN,
        {
            DOMAIN: {
                CONF_HOST: "localhost",
                CONF_PORT: "9000",
                CONF_ACCESS_KEY: "abcdef",
                CONF_SECRET_KEY: "0123456789",
                CONF_SECURE: "true",
            }
        },
    )

    await hass.async_start()
    await hass.async_block_till_done()

    assert "Setup of domain minio took" in caplog.text

    # Call services
    await hass.services.async_call(
        DOMAIN,
        "put",
        {"file_path": "/test/some_file", "key": "some_key", "bucket": "some_bucket"},
        blocking=True,
    )
    assert minio_client.fput_object.call_args == call(
        "some_bucket", "some_key", "/test/some_file"
    )
    minio_client.reset_mock()

    await hass.services.async_call(
        DOMAIN,
        "get",
        {"file_path": "/test/some_file", "key": "some_key", "bucket": "some_bucket"},
        blocking=True,
    )
    assert minio_client.fget_object.call_args == call(
        "some_bucket", "some_key", "/test/some_file"
    )
    minio_client.reset_mock()

    await hass.services.async_call(
        DOMAIN, "remove", {"key": "some_key", "bucket": "some_bucket"}, blocking=True
    )
    assert minio_client.remove_object.call_args == call("some_bucket", "some_key")
    minio_client.reset_mock()


async def test_minio_listen(hass, caplog, minio_client_event):
    """Test minio listen on notifications."""
    minio_client_event.presigned_get_object.return_value = "http://url"

    events = []

    @callback
    def event_callback(event):
        """Handle event callbback."""
        events.append(event)

    hass.bus.async_listen("minio", event_callback)

    await async_setup_component(
        hass,
        DOMAIN,
        {
            DOMAIN: {
                CONF_HOST: "localhost",
                CONF_PORT: "9000",
                CONF_ACCESS_KEY: "abcdef",
                CONF_SECRET_KEY: "0123456789",
                CONF_SECURE: "true",
                CONF_LISTEN: [{CONF_LISTEN_BUCKET: "test"}],
            }
        },
    )

    await hass.async_start()
    await hass.async_block_till_done()

    assert "Setup of domain minio took" in caplog.text

    while not events:
        await asyncio.sleep(0)

    assert len(events) == 1
    event = events[0]

    assert event.event_type == DOMAIN
    assert event.data["event_name"] == "s3:ObjectCreated:Put"
    assert event.data["file_name"] == "5jJkTAo.jpg"
    assert event.data["bucket"] == "test"
    assert event.data["key"] == "5jJkTAo.jpg"
    assert event.data["presigned_url"] == "http://url"
    assert len(event.data["metadata"]) == 0


async def test_queue_listener():
    """Tests QueueListener firing events on Home Assistant event bus."""
    hass = MagicMock()

    queue_listener = QueueListener(hass)
    queue_listener.start()

    queue_entry = {
        "event_name": "s3:ObjectCreated:Put",
        "bucket": "some_bucket",
        "key": "some_dir/some_file.jpg",
        "presigned_url": "http://host/url?signature=secret",
        "metadata": {},
    }

    queue_listener.queue.put(queue_entry)

    queue_listener.stop()

    call_domain, call_event = hass.bus.fire.call_args[0]

    expected_event = {
        "event_name": "s3:ObjectCreated:Put",
        "file_name": "some_file.jpg",
        "bucket": "some_bucket",
        "key": "some_dir/some_file.jpg",
        "presigned_url": "http://host/url?signature=secret",
        "metadata": {},
    }

    assert call_domain == DOMAIN
    assert json.dumps(expected_event, sort_keys=True) == json.dumps(
        call_event, sort_keys=True
    )