"""Support for Velbus devices."""

from __future__ import annotations

import os
import shutil
from typing import TYPE_CHECKING

import voluptuous as vol

from homeassistant.const import CONF_ADDRESS
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv, selector, service
from homeassistant.helpers.storage import STORAGE_DIR

if TYPE_CHECKING:
    from . import VelbusConfigEntry

from .const import (
    CONF_CONFIG_ENTRY,
    CONF_MEMO_TEXT,
    DOMAIN,
    SERVICE_CLEAR_CACHE,
    SERVICE_SCAN,
    SERVICE_SET_MEMO_TEXT,
    SERVICE_SYNC,
)


@callback
def async_setup_services(hass: HomeAssistant) -> None:
    """Register the velbus services."""

    async def scan(call: ServiceCall) -> None:
        """Handle a scan service call."""
        entry: VelbusConfigEntry = service.async_get_config_entry(
            call.hass, DOMAIN, call.data[CONF_CONFIG_ENTRY]
        )
        try:
            await entry.runtime_data.controller.scan()
        except OSError as exc:
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="scan_failed",
                translation_placeholders={"error": str(exc)},
            ) from exc

    async def syn_clock(call: ServiceCall) -> None:
        """Handle a sync clock service call."""
        entry: VelbusConfigEntry = service.async_get_config_entry(
            call.hass, DOMAIN, call.data[CONF_CONFIG_ENTRY]
        )
        try:
            await entry.runtime_data.controller.sync_clock()
        except OSError as exc:
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="sync_clock_failed",
                translation_placeholders={"error": str(exc)},
            ) from exc

    async def set_memo_text(call: ServiceCall) -> None:
        """Handle Memo Text service call."""
        entry: VelbusConfigEntry = service.async_get_config_entry(
            call.hass, DOMAIN, call.data[CONF_CONFIG_ENTRY]
        )
        memo_text = call.data[CONF_MEMO_TEXT]
        address = call.data[CONF_ADDRESS]
        module = entry.runtime_data.controller.get_module(address)
        if not module:
            raise ServiceValidationError(
                translation_domain=DOMAIN,
                translation_key="module_not_found",
                translation_placeholders={"address": str(address)},
            )
        try:
            await module.set_memo_text(memo_text)
        except OSError as exc:
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="set_memo_text_failed",
                translation_placeholders={"error": str(exc)},
            ) from exc

    async def clear_cache(call: ServiceCall) -> None:
        """Handle a clear cache service call."""
        entry: VelbusConfigEntry = service.async_get_config_entry(
            call.hass, DOMAIN, call.data[CONF_CONFIG_ENTRY]
        )
        try:
            if call.data.get(CONF_ADDRESS):
                await hass.async_add_executor_job(
                    os.unlink,
                    hass.config.path(
                        STORAGE_DIR,
                        f"velbuscache-{entry.entry_id}/{call.data[CONF_ADDRESS]}.p",
                    ),
                )
            else:
                await hass.async_add_executor_job(
                    shutil.rmtree,
                    hass.config.path(STORAGE_DIR, f"velbuscache-{entry.entry_id}/"),
                )
        except FileNotFoundError:
            pass  # It's okay if the file doesn't exist
        except OSError as exc:
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="clear_cache_failed",
                translation_placeholders={"error": str(exc)},
            ) from exc
        # call a scan to repopulate
        await scan(call)

    hass.services.async_register(
        DOMAIN,
        SERVICE_SCAN,
        scan,
        vol.Schema(
            {
                vol.Required(CONF_CONFIG_ENTRY): selector.ConfigEntrySelector(
                    {
                        "integration": DOMAIN,
                    }
                )
            }
        ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_SYNC,
        syn_clock,
        vol.Schema(
            {
                vol.Required(CONF_CONFIG_ENTRY): selector.ConfigEntrySelector(
                    {
                        "integration": DOMAIN,
                    }
                )
            }
        ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_SET_MEMO_TEXT,
        set_memo_text,
        vol.Schema(
            {
                vol.Required(CONF_CONFIG_ENTRY): selector.ConfigEntrySelector(
                    {
                        "integration": DOMAIN,
                    }
                ),
                vol.Required(CONF_ADDRESS): vol.All(
                    vol.Coerce(int), vol.Range(min=0, max=255)
                ),
                vol.Optional(CONF_MEMO_TEXT, default=""): cv.string,
            }
        ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_CLEAR_CACHE,
        clear_cache,
        vol.Schema(
            {
                vol.Required(CONF_CONFIG_ENTRY): selector.ConfigEntrySelector(
                    {
                        "integration": DOMAIN,
                    }
                ),
                vol.Optional(CONF_ADDRESS): vol.All(
                    vol.Coerce(int), vol.Range(min=0, max=255)
                ),
            }
        ),
    )
