"""Config flow to configure the Bluetooth integration."""

from __future__ import annotations

import platform
from typing import Any, cast

from bluetooth_adapters import (
    ADAPTER_ADDRESS,
    ADAPTER_MANUFACTURER,
    DEFAULT_ADDRESS,
    AdapterDetails,
    adapter_human_name,
    adapter_model,
    get_adapters,
)
from habluetooth import get_manager
import voluptuous as vol

from homeassistant.components import onboarding
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
from homeassistant.core import callback
from homeassistant.helpers.schema_config_entry_flow import (
    SchemaFlowFormStep,
    SchemaOptionsFlowHandler,
)
from homeassistant.helpers.typing import DiscoveryInfoType

from .const import CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DOMAIN
from .util import adapter_title

OPTIONS_SCHEMA = vol.Schema(
    {
        vol.Required(CONF_PASSIVE, default=False): bool,
    }
)
OPTIONS_FLOW = {
    "init": SchemaFlowFormStep(OPTIONS_SCHEMA),
}


def adapter_display_info(adapter: str, details: AdapterDetails) -> str:
    """Return the adapter display info."""
    name = adapter_human_name(adapter, details[ADAPTER_ADDRESS])
    model = adapter_model(details)
    manufacturer = details[ADAPTER_MANUFACTURER] or "Unknown"
    return f"{name} {manufacturer} {model}"


class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
    """Config flow for Bluetooth."""

    VERSION = 1

    def __init__(self) -> None:
        """Initialize the config flow."""
        self._adapter: str | None = None
        self._details: AdapterDetails | None = None
        self._adapters: dict[str, AdapterDetails] = {}
        self._placeholders: dict[str, str] = {}

    async def async_step_integration_discovery(
        self, discovery_info: DiscoveryInfoType
    ) -> ConfigFlowResult:
        """Handle a flow initialized by discovery."""
        self._adapter = cast(str, discovery_info[CONF_ADAPTER])
        self._details = cast(AdapterDetails, discovery_info[CONF_DETAILS])
        await self.async_set_unique_id(self._details[ADAPTER_ADDRESS])
        self._abort_if_unique_id_configured()
        details = self._details
        self._async_set_adapter_info(self._adapter, details)
        return await self.async_step_single_adapter()

    @callback
    def _async_set_adapter_info(self, adapter: str, details: AdapterDetails) -> None:
        """Set the adapter info."""
        name = adapter_human_name(adapter, details[ADAPTER_ADDRESS])
        model = adapter_model(details)
        manufacturer = details[ADAPTER_MANUFACTURER]
        self._placeholders = {
            "name": name,
            "model": model,
            "manufacturer": manufacturer or "Unknown",
        }
        self.context["title_placeholders"] = self._placeholders

    async def async_step_single_adapter(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Select an adapter."""
        adapter = self._adapter
        details = self._details
        assert adapter is not None
        assert details is not None
        assert self._placeholders is not None

        address = details[ADAPTER_ADDRESS]

        if user_input is not None or not onboarding.async_is_onboarded(self.hass):
            await self.async_set_unique_id(address, raise_on_progress=False)
            self._abort_if_unique_id_configured()
            return self.async_create_entry(
                title=adapter_title(adapter, details), data={}
            )

        return self.async_show_form(
            step_id="single_adapter",
            description_placeholders=self._placeholders,
        )

    async def async_step_multiple_adapters(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Handle a flow initialized by the user."""
        if user_input is not None:
            assert self._adapters is not None
            adapter = user_input[CONF_ADAPTER]
            details = self._adapters[adapter]
            address = details[ADAPTER_ADDRESS]
            await self.async_set_unique_id(address, raise_on_progress=False)
            self._abort_if_unique_id_configured()
            return self.async_create_entry(
                title=adapter_title(adapter, details), data={}
            )

        configured_addresses = self._async_current_ids()
        bluetooth_adapters = get_adapters()
        await bluetooth_adapters.refresh()
        self._adapters = bluetooth_adapters.adapters
        system = platform.system()
        unconfigured_adapters = [
            adapter
            for adapter, details in self._adapters.items()
            if details[ADAPTER_ADDRESS] not in configured_addresses
            # DEFAULT_ADDRESS is perfectly valid on MacOS but on
            # Linux it means the adapter is not yet configured
            # or crashed
            and not (system == "Linux" and details[ADAPTER_ADDRESS] == DEFAULT_ADDRESS)
        ]
        if not unconfigured_adapters:
            ignored_adapters = len(
                self._async_current_entries(include_ignore=True)
            ) - len(self._async_current_entries(include_ignore=False))
            return self.async_abort(
                reason="no_adapters",
                description_placeholders={"ignored_adapters": str(ignored_adapters)},
            )
        if len(unconfigured_adapters) == 1:
            self._adapter = list(self._adapters)[0]
            self._details = self._adapters[self._adapter]
            self._async_set_adapter_info(self._adapter, self._details)
            return await self.async_step_single_adapter()

        return self.async_show_form(
            step_id="multiple_adapters",
            data_schema=vol.Schema(
                {
                    vol.Required(CONF_ADAPTER): vol.In(
                        {
                            adapter: adapter_display_info(
                                adapter, self._adapters[adapter]
                            )
                            for adapter in sorted(unconfigured_adapters)
                        }
                    ),
                }
            ),
        )

    async def async_step_user(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Handle a flow initialized by the user."""
        return await self.async_step_multiple_adapters()

    @staticmethod
    @callback
    def async_get_options_flow(
        config_entry: ConfigEntry,
    ) -> SchemaOptionsFlowHandler:
        """Get the options flow for this handler."""
        return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)

    @classmethod
    @callback
    def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
        """Return options flow support for this handler."""
        return bool((manager := get_manager()) and manager.supports_passive_scan)