"""Test supervisor repairs."""

from http import HTTPStatus
import os
from unittest.mock import patch

import pytest

from homeassistant.core import HomeAssistant
import homeassistant.helpers.issue_registry as ir
from homeassistant.setup import async_setup_component

from .test_init import MOCK_ENVIRON
from .test_issues import mock_resolution_info

from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator


@pytest.fixture(autouse=True)
async def fixture_supervisor_environ():
    """Mock os environ for supervisor."""
    with patch.dict(os.environ, MOCK_ENVIRON):
        yield


async def test_supervisor_issue_repair_flow(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test fix flow for supervisor issue."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "multiple_data_disks",
                "context": "system",
                "reference": "/dev/sda1",
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "rename_data_disk",
                        "context": "system",
                        "reference": "/dev/sda1",
                    }
                ],
            },
        ],
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "form",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "system_rename_data_disk",
        "data_schema": [],
        "errors": None,
        "description_placeholders": {"reference": "/dev/sda1"},
        "last_step": True,
        "preview": None,
    }

    resp = await client.post(f"/api/repairs/issues/fix/{flow_id}")

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "create_entry",
        "flow_id": flow_id,
        "handler": "hassio",
        "description": None,
        "description_placeholders": None,
    }

    assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")

    assert aioclient_mock.mock_calls[-1][0] == "post"
    assert (
        str(aioclient_mock.mock_calls[-1][1])
        == "http://127.0.0.1/resolution/suggestion/1235"
    )


async def test_supervisor_issue_repair_flow_with_multiple_suggestions(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test fix flow for supervisor issue with multiple suggestions."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "reboot_required",
                "context": "system",
                "reference": "test",
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "execute_reboot",
                        "context": "system",
                        "reference": "test",
                    },
                    {
                        "uuid": "1236",
                        "type": "test_type",
                        "context": "system",
                        "reference": "test",
                    },
                ],
            },
        ],
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "menu",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "fix_menu",
        "data_schema": [
            {
                "type": "select",
                "options": [
                    ["system_execute_reboot", "system_execute_reboot"],
                    ["system_test_type", "system_test_type"],
                ],
                "name": "next_step_id",
            }
        ],
        "menu_options": ["system_execute_reboot", "system_test_type"],
        "description_placeholders": {"reference": "test"},
    }

    resp = await client.post(
        f"/api/repairs/issues/fix/{flow_id}", json={"next_step_id": "system_test_type"}
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "create_entry",
        "flow_id": flow_id,
        "handler": "hassio",
        "description": None,
        "description_placeholders": None,
    }

    assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")

    assert aioclient_mock.mock_calls[-1][0] == "post"
    assert (
        str(aioclient_mock.mock_calls[-1][1])
        == "http://127.0.0.1/resolution/suggestion/1236"
    )


async def test_supervisor_issue_repair_flow_with_multiple_suggestions_and_confirmation(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test fix flow for supervisor issue with multiple suggestions and choice requires confirmation."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "reboot_required",
                "context": "system",
                "reference": None,
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "execute_reboot",
                        "context": "system",
                        "reference": None,
                    },
                    {
                        "uuid": "1236",
                        "type": "test_type",
                        "context": "system",
                        "reference": None,
                    },
                ],
            },
        ],
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "menu",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "fix_menu",
        "data_schema": [
            {
                "type": "select",
                "options": [
                    ["system_execute_reboot", "system_execute_reboot"],
                    ["system_test_type", "system_test_type"],
                ],
                "name": "next_step_id",
            }
        ],
        "menu_options": ["system_execute_reboot", "system_test_type"],
        "description_placeholders": None,
    }

    resp = await client.post(
        f"/api/repairs/issues/fix/{flow_id}",
        json={"next_step_id": "system_execute_reboot"},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "form",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "system_execute_reboot",
        "data_schema": [],
        "errors": None,
        "description_placeholders": None,
        "last_step": True,
        "preview": None,
    }

    resp = await client.post(f"/api/repairs/issues/fix/{flow_id}")

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "create_entry",
        "flow_id": flow_id,
        "handler": "hassio",
        "description": None,
        "description_placeholders": None,
    }

    assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")

    assert aioclient_mock.mock_calls[-1][0] == "post"
    assert (
        str(aioclient_mock.mock_calls[-1][1])
        == "http://127.0.0.1/resolution/suggestion/1235"
    )


async def test_supervisor_issue_repair_flow_skip_confirmation(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test confirmation skipped for fix flow for supervisor issue with one suggestion."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "reboot_required",
                "context": "system",
                "reference": None,
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "execute_reboot",
                        "context": "system",
                        "reference": None,
                    }
                ],
            },
        ],
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "form",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "system_execute_reboot",
        "data_schema": [],
        "errors": None,
        "description_placeholders": None,
        "last_step": True,
        "preview": None,
    }

    resp = await client.post(f"/api/repairs/issues/fix/{flow_id}")

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "create_entry",
        "flow_id": flow_id,
        "handler": "hassio",
        "description": None,
        "description_placeholders": None,
    }

    assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")

    assert aioclient_mock.mock_calls[-1][0] == "post"
    assert (
        str(aioclient_mock.mock_calls[-1][1])
        == "http://127.0.0.1/resolution/suggestion/1235"
    )


async def test_mount_failed_repair_flow_error(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test repair flow fails when repair fails to apply."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "mount_failed",
                "context": "mount",
                "reference": "backup_share",
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "execute_reload",
                        "context": "mount",
                        "reference": "backup_share",
                    },
                    {
                        "uuid": "1236",
                        "type": "execute_remove",
                        "context": "mount",
                        "reference": "backup_share",
                    },
                ],
            },
        ],
        suggestion_result=False,
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()
    flow_id = data["flow_id"]

    resp = await client.post(
        f"/api/repairs/issues/fix/{flow_id}",
        json={"next_step_id": "mount_execute_reload"},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "abort",
        "flow_id": flow_id,
        "handler": "hassio",
        "reason": "apply_suggestion_fail",
        "result": None,
        "description_placeholders": None,
    }

    assert issue_registry.async_get_issue(domain="hassio", issue_id="1234")


async def test_mount_failed_repair_flow(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test repair flow for mount_failed issue."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "mount_failed",
                "context": "mount",
                "reference": "backup_share",
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "execute_reload",
                        "context": "mount",
                        "reference": "backup_share",
                    },
                    {
                        "uuid": "1236",
                        "type": "execute_remove",
                        "context": "mount",
                        "reference": "backup_share",
                    },
                ],
            },
        ],
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "menu",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "fix_menu",
        "data_schema": [
            {
                "type": "select",
                "options": [
                    ["mount_execute_reload", "mount_execute_reload"],
                    ["mount_execute_remove", "mount_execute_remove"],
                ],
                "name": "next_step_id",
            }
        ],
        "menu_options": ["mount_execute_reload", "mount_execute_remove"],
        "description_placeholders": {
            "reference": "backup_share",
            "storage_url": "/config/storage",
        },
    }

    resp = await client.post(
        f"/api/repairs/issues/fix/{flow_id}",
        json={"next_step_id": "mount_execute_reload"},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "create_entry",
        "flow_id": flow_id,
        "handler": "hassio",
        "description": None,
        "description_placeholders": None,
    }

    assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")

    assert aioclient_mock.mock_calls[-1][0] == "post"
    assert (
        str(aioclient_mock.mock_calls[-1][1])
        == "http://127.0.0.1/resolution/suggestion/1235"
    )


@pytest.mark.parametrize(
    "all_setup_requests", [{"include_addons": True}], indirect=True
)
async def test_supervisor_issue_docker_config_repair_flow(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test fix flow for supervisor issue."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "docker_config",
                "context": "system",
                "reference": None,
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "execute_rebuild",
                        "context": "system",
                        "reference": None,
                    }
                ],
            },
            {
                "uuid": "1236",
                "type": "docker_config",
                "context": "core",
                "reference": None,
                "suggestions": [
                    {
                        "uuid": "1237",
                        "type": "execute_rebuild",
                        "context": "core",
                        "reference": None,
                    }
                ],
            },
            {
                "uuid": "1238",
                "type": "docker_config",
                "context": "addon",
                "reference": "test",
                "suggestions": [
                    {
                        "uuid": "1239",
                        "type": "execute_rebuild",
                        "context": "addon",
                        "reference": "test",
                    }
                ],
            },
        ],
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "form",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "system_execute_rebuild",
        "data_schema": [],
        "errors": None,
        "description_placeholders": {"components": "Home Assistant\n- test"},
        "last_step": True,
        "preview": None,
    }

    resp = await client.post(f"/api/repairs/issues/fix/{flow_id}")

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "create_entry",
        "flow_id": flow_id,
        "handler": "hassio",
        "description": None,
        "description_placeholders": None,
    }

    assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")

    assert aioclient_mock.mock_calls[-1][0] == "post"
    assert (
        str(aioclient_mock.mock_calls[-1][1])
        == "http://127.0.0.1/resolution/suggestion/1235"
    )


async def test_supervisor_issue_repair_flow_multiple_data_disks(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test fix flow for multiple data disks supervisor issue."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "multiple_data_disks",
                "context": "system",
                "reference": "/dev/sda1",
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "rename_data_disk",
                        "context": "system",
                        "reference": "/dev/sda1",
                    },
                    {
                        "uuid": "1236",
                        "type": "adopt_data_disk",
                        "context": "system",
                        "reference": "/dev/sda1",
                    },
                ],
            },
        ],
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "menu",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "fix_menu",
        "data_schema": [
            {
                "type": "select",
                "options": [
                    ["system_rename_data_disk", "system_rename_data_disk"],
                    ["system_adopt_data_disk", "system_adopt_data_disk"],
                ],
                "name": "next_step_id",
            }
        ],
        "menu_options": ["system_rename_data_disk", "system_adopt_data_disk"],
        "description_placeholders": {"reference": "/dev/sda1"},
    }

    resp = await client.post(
        f"/api/repairs/issues/fix/{flow_id}",
        json={"next_step_id": "system_adopt_data_disk"},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "form",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "system_adopt_data_disk",
        "data_schema": [],
        "errors": None,
        "description_placeholders": {"reference": "/dev/sda1"},
        "last_step": True,
        "preview": None,
    }

    resp = await client.post(f"/api/repairs/issues/fix/{flow_id}")

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "create_entry",
        "flow_id": flow_id,
        "handler": "hassio",
        "description": None,
        "description_placeholders": None,
    }

    assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")

    assert aioclient_mock.mock_calls[-1][0] == "post"
    assert (
        str(aioclient_mock.mock_calls[-1][1])
        == "http://127.0.0.1/resolution/suggestion/1236"
    )


@pytest.mark.parametrize(
    "all_setup_requests", [{"include_addons": True}], indirect=True
)
async def test_supervisor_issue_detached_addon_removed(
    hass: HomeAssistant,
    aioclient_mock: AiohttpClientMocker,
    hass_client: ClientSessionGenerator,
    issue_registry: ir.IssueRegistry,
    all_setup_requests,
) -> None:
    """Test fix flow for supervisor issue."""
    mock_resolution_info(
        aioclient_mock,
        issues=[
            {
                "uuid": "1234",
                "type": "detached_addon_removed",
                "context": "addon",
                "reference": "test",
                "suggestions": [
                    {
                        "uuid": "1235",
                        "type": "execute_remove",
                        "context": "addon",
                        "reference": "test",
                    }
                ],
            },
        ],
    )

    assert await async_setup_component(hass, "hassio", {})

    repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
    assert repair_issue

    client = await hass_client()

    resp = await client.post(
        "/api/repairs/issues/fix",
        json={"handler": "hassio", "issue_id": repair_issue.issue_id},
    )

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "form",
        "flow_id": flow_id,
        "handler": "hassio",
        "step_id": "addon_execute_remove",
        "data_schema": [],
        "errors": None,
        "description_placeholders": {
            "reference": "test",
            "addon": "test",
            "help_url": "https://www.home-assistant.io/help/",
            "community_url": "https://community.home-assistant.io/",
        },
        "last_step": True,
        "preview": None,
    }

    resp = await client.post(f"/api/repairs/issues/fix/{flow_id}")

    assert resp.status == HTTPStatus.OK
    data = await resp.json()

    flow_id = data["flow_id"]
    assert data == {
        "type": "create_entry",
        "flow_id": flow_id,
        "handler": "hassio",
        "description": None,
        "description_placeholders": None,
    }

    assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")

    assert aioclient_mock.mock_calls[-1][0] == "post"
    assert (
        str(aioclient_mock.mock_calls[-1][1])
        == "http://127.0.0.1/resolution/suggestion/1235"
    )