"""State management module for Actron Air systems."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any, Callable

from .models import ActronAirStatus

if TYPE_CHECKING:
    from .actron import ActronAirAPI

_LOGGER = logging.getLogger(__name__)


class StateManager:
    """Manages the state of Actron Air systems, handling updates and state merging."""

    def __init__(self) -> None:
        """Initialize the state manager.

        Creates empty dictionaries for system status tracking
        and initializes observer list for state change notifications.
        """
        self.status: dict[str, ActronAirStatus] = {}
        self._observers: list[Callable[[str, dict[str, Any]], None]] = []
        self._api: "ActronAirAPI | None" = None

    def set_api(self, api: "ActronAirAPI") -> None:
        """Set the API reference to be passed to status objects.

        Args:
            api: Reference to the ActronAirAPI instance

        """
        self._api = api

        # Update existing status objects with the API reference
        for status in self.status.values():
            status.set_api(api)

    def add_observer(self, observer: Callable[[str, dict[str, Any]], None]) -> None:
        """Add an observer to be notified of state changes.

        Duplicate observers are silently ignored.

        Args:
            observer: Callback function that takes (serial_number, status_data)

        """
        if observer not in self._observers:
            self._observers.append(observer)

    def remove_observer(self, observer: Callable[[str, dict[str, Any]], None]) -> None:
        """Remove a previously registered observer.

        Args:
            observer: The callback to remove

        """
        try:
            self._observers.remove(observer)
        except ValueError:
            pass

    def get_status(self, serial_number: str) -> ActronAirStatus | None:
        """Get the status for a specific system.

        Args:
            serial_number: Serial number of the AC system

        Returns:
            ActronAirStatus object if found, None otherwise

        """
        # Normalize serial number to lowercase for consistent lookup
        return self.status.get(serial_number.lower())

    def process_status_update(
        self, serial_number: str, status_data: dict[str, Any] | ActronAirStatus
    ) -> ActronAirStatus:
        """Process a full status update for a system.

        Args:
            serial_number: Serial number of the AC system
            status_data: Complete status data from API or ActronAirStatus object

        Returns:
            Updated ActronAirStatus object

        Note:
            This parses nested components, maps peripheral data to zones,
            and notifies all registered observers.

        """
        if isinstance(status_data, ActronAirStatus):
            status = status_data
            raw_data = status.last_known_state
        else:
            status = ActronAirStatus.model_validate(status_data)
            status.parse_nested_components()
            raw_data = status_data

        # Normalize serial number to lowercase for consistent storage
        serial_number = serial_number.lower()

        # Set serial number and API reference
        status.serial_number = serial_number
        if self._api:
            status.set_api(self._api)

        self.status[serial_number] = status

        # Notify observers with a shallow copy — don't let observer errors break the update
        for observer in self._observers:
            try:
                observer(serial_number, dict(raw_data) if isinstance(raw_data, dict) else raw_data)
            except Exception as e:
                _LOGGER.warning(
                    "Observer callback failed for %s: %s",
                    serial_number,
                    e,
                    exc_info=True,
                )

        return status
