import reactivex.operators as ops
import voluptuous as vol
from homeassistant.components.dhcp import DhcpServiceInfo
from homeassistant.config_entries import ConfigFlow
from homeassistant.const import CONF_HOST, CONF_MAC

from . import CONF_PRODUCT, DOMAIN, request
from .ecoflow import PORT, PRODUCTS, receive, send
from .ecoflow.rxtcp import RxTcpAutoConnection


class EcoflowConfigFlow(ConfigFlow, domain=DOMAIN):
    VERSION = 1
    host = None
    mac = None

    async def _get_serial_main(self):
        tcp = RxTcpAutoConnection(self.host, PORT)
        received = tcp.received.pipe(
            receive.merge_packet(),
            ops.map(receive.decode_packet),
            ops.filter(receive.is_serial_main),
            ops.map(lambda x: receive.parse_serial(x[3])),
        )
        try:
            await tcp.wait_opened()
            info = await request(tcp, send.get_serial_main(), received)
        finally:
            tcp.close()
        if info["product"] not in PRODUCTS:
            return self.async_abort(reason="product_unsupported")
        await self.async_set_unique_id(info["serial"])
        self._abort_if_unique_id_configured(updates={
            CONF_HOST: self.host,
            CONF_MAC: self.mac,
        })
        return info

    async def async_step_dhcp(self, discovery_info: DhcpServiceInfo):
        self.host = discovery_info.ip
        self.mac = discovery_info.macaddress
        await self._get_serial_main()
        return self.async_show_form(step_id="user")

    async def async_step_user(self, user_input: dict = None):
        if user_input:
            self.host = user_input.get(CONF_HOST)

        errors = {}
        if self.host and user_input is not None:
            try:
                info = await self._get_serial_main()
            except TimeoutError:
                errors["base"] = "timeout"
            else:
                pn = PRODUCTS.get(info["product"], "")
                if pn != "":
                    pn += " "
                return self.async_create_entry(
                    title=f'{pn}{info["serial"][-6:]}',
                    data={
                        CONF_HOST: self.host,
                        CONF_MAC: self.mac,
                        CONF_PRODUCT: info["product"],
                    },
                )

        return self.async_show_form(
            step_id="user",
            errors=errors,
            data_schema=vol.Schema({
                vol.Required(CONF_HOST, default=self.host): str,
            }),
            last_step=True,
        )
