"""Config flow for IronOS integration."""

import logging
from typing import Any, override

from bleak.exc import BleakError
from habluetooth import BluetoothServiceInfoBleak
from pynecil import CommunicationError, Pynecil
import voluptuous as vol

from homeassistant.components.bluetooth.api import async_discovered_service_info
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_ADDRESS

from .const import DISCOVERY_SVC_UUID, DOMAIN

_LOGGER = logging.getLogger(__name__)


class IronOSConfigFlow(ConfigFlow, domain=DOMAIN):
    """Handle a config flow for IronOS."""

    def __init__(self) -> None:
        """Initialize the config flow."""
        self._discovery_info: BluetoothServiceInfoBleak | None = None
        self._discovered_devices: dict[str, str] = {}

    @override
    async def async_step_bluetooth(
        self, discovery_info: BluetoothServiceInfoBleak
    ) -> ConfigFlowResult:
        """Handle bluetooth discovery step."""
        await self.async_set_unique_id(discovery_info.address)
        self._abort_if_unique_id_configured()
        self._discovery_info = discovery_info

        return await self.async_step_bluetooth_confirm()

    async def async_step_bluetooth_confirm(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Confirm discovery."""

        errors: dict[str, str] = {}

        assert self._discovery_info is not None
        discovery_info = self._discovery_info
        title = discovery_info.name

        if user_input is not None:
            device = Pynecil(discovery_info.address)
            try:
                await device.connect()
            except CommunicationError, BleakError, TimeoutError:
                _LOGGER.debug("Cannot connect:", exc_info=True)
                errors["base"] = "cannot_connect"
            except Exception:
                _LOGGER.exception("Unexpected exception:")
                errors["base"] = "unknown"
            else:
                return self.async_create_entry(title=title, data={})
            finally:
                await device.disconnect()

        self._set_confirm_only()
        placeholders = {"name": title}
        self.context["title_placeholders"] = placeholders
        return self.async_show_form(
            step_id="bluetooth_confirm",
            description_placeholders=placeholders,
            errors=errors,
        )

    @override
    async def async_step_user(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Handle the user step to pick discovered device."""

        errors: dict[str, str] = {}

        if user_input is not None:
            address = user_input[CONF_ADDRESS]
            title = self._discovered_devices[address]
            await self.async_set_unique_id(address, raise_on_progress=False)
            self._abort_if_unique_id_configured()
            device = Pynecil(address)
            try:
                await device.connect()
            except CommunicationError, BleakError, TimeoutError:
                _LOGGER.debug("Cannot connect:", exc_info=True)
                errors["base"] = "cannot_connect"
            except Exception:
                _LOGGER.exception("Unexpected exception")
                errors["base"] = "unknown"
            else:
                return self.async_create_entry(title=title, data={})
            finally:
                await device.disconnect()

        current_addresses = self._async_current_ids(include_ignore=False)
        for discovery_info in async_discovered_service_info(self.hass, True):
            address = discovery_info.address
            if (
                DISCOVERY_SVC_UUID not in discovery_info.service_uuids
                or address in current_addresses
                or address in self._discovered_devices
            ):
                continue
            self._discovered_devices[address] = discovery_info.name

        if not self._discovered_devices:
            return self.async_abort(reason="no_devices_found")

        return self.async_show_form(
            step_id="user",
            data_schema=vol.Schema(
                {vol.Required(CONF_ADDRESS): vol.In(self._discovered_devices)}
            ),
            errors=errors,
        )
