"""Master class that combines all ElkM1 pieces together."""

from __future__ import annotations

import asyncio
import logging
from typing import Any

from .areas import Areas
from .connection import Connection
from .counters import Counters
from .keypads import Keypads
from .lights import Lights
from .message import MessageEncode, MsgHandler, ua_encode
from .notify import Notifier
from .outputs import Outputs
from .panel import Panel
from .settings import Settings
from .tasks import Tasks
from .thermostats import Thermostats
from .users import Users
from .util import url_scheme_is_secure
from .zones import Zones

LOG = logging.getLogger(__name__)


class Elk:
    """Represents all the components on an Elk panel."""

    def __init__(
        self, config: dict[str, Any], loop: asyncio.AbstractEventLoop | None = None
    ) -> None:
        """Initialize a new Elk instance."""
        self._config = config
        if not loop:
            try:
                loop = asyncio.get_running_loop()
            except RuntimeError:
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
        self._loop = loop

        self._notifier = Notifier()
        self._connection = Connection(config["url"], self._notifier)
        self._logged_in = False

        # Setup for all the types of elements tracked
        if "element_list" in config:
            self.element_list = config["element_list"]
        else:
            self.element_list = [
                "panel",
                "zones",
                "lights",
                "areas",
                "tasks",
                "keypads",
                "outputs",
                "thermostats",
                "counters",
                "settings",
                "users",
            ]

        self._notifier.attach("connected", self._connected)
        self._notifier.attach("disconnected", self._disconnected)
        self._notifier.attach("login", self._login_status)
        self._notifier.attach("IE", self._call_sync_handlers)
        self._notifier.attach("VN", self._got_first_message)

        self.areas = Areas(self._connection, self._notifier)
        self.counters = Counters(self._connection, self._notifier)
        self.keypads = Keypads(self._connection, self._notifier)
        self.lights = Lights(self._connection, self._notifier)
        self.outputs = Outputs(self._connection, self._notifier)
        self.panel = Panel(self._connection, self._notifier)
        self.settings = Settings(self._connection, self._notifier)
        self.tasks = Tasks(self._connection, self._notifier)
        self.thermostats = Thermostats(self._connection, self._notifier)
        self.users = Users(self._connection, self._notifier)
        self.zones = Zones(self._connection, self._notifier)

    def _login_status(self, succeeded: bool) -> None:
        self._logged_in = succeeded
        if not succeeded:
            self._connection.disconnect()
            LOG.error("Invalid username or password.")

    def _got_first_message(self, **kwargs: dict[str, Any]) -> None:
        if not self._logged_in:
            self._notifier.notify("login", {"succeeded": True})

    def _connected(self) -> None:
        if url_scheme_is_secure(self._config["url"]):
            self._connection.send_raw(self._config["userid"])
            self._connection.send_raw(self._config["password"])
        self._call_sync_handlers()

    def _disconnected(self) -> None:
        self._logged_in = False

    def _sync_complete(self, **_: dict[str, Any]) -> None:
        self._notifier.notify("sync_complete", {})

        # Remove so that other apps can send UA and not trigger sync_complete
        self._notifier.detach("UA", self._sync_complete)

    def _call_sync_handlers(self) -> None:
        """Invoke the synchronization handlers."""

        LOG.debug("Synchronizing panel...")
        self.add_handler("UA", self._sync_complete)
        for element in self.element_list:
            getattr(self, element).sync()
        self.send(ua_encode(0))  # Used to mark end of sync

    @property
    def connection(self) -> Connection:
        """Return the connection instance."""
        return self._connection

    def run(self) -> None:
        """Enter the asyncio loop."""
        self._loop.run_forever()

    def add_handler(self, msg_type: str, handler: MsgHandler) -> None:
        """Helper to connection add_handler."""
        self._notifier.attach(msg_type, handler)

    def remove_handler(self, msg_type: str, handler: MsgHandler) -> None:
        """Helper to connection remove_handler."""
        self._notifier.detach(msg_type, handler)

    def connect(self) -> None:
        """Helper to connection connect."""
        asyncio.ensure_future(self._connection.connect())

    def disconnect(self) -> None:
        """Helper to connection disconnect."""
        self._connection.disconnect()

    def is_connected(self) -> bool:
        """Helper to connection is_connected."""
        return self._connection.is_connected()

    def is_paused(self) -> bool:
        """Helper to connection is_paused."""
        return self._connection.is_paused()

    def send(self, msg: MessageEncode) -> None:
        """Helper to connection send."""
        self._connection.send(msg)
