"""Define a SmartCocoon Fan class."""

import logging
from datetime import datetime
from typing import Any, Optional

from pysmartcocoon.api import SmartCocoonAPI
from pysmartcocoon.const import FanMode
from pysmartcocoon.fan_helpers import derive_mode_from_speed, resolve_speed

_LOGGER: logging.Logger = logging.getLogger(__name__)


# pylint: disable=too-many-instance-attributes, too-many-public-methods
class Fan:
    """Define the fan."""

    def __init__(
        self,
        fan_id: str,
        api: SmartCocoonAPI,
    ) -> None:
        """Initialize."""

        # Fan attributes from SmartCocoon
        self._fan_id: str = fan_id
        self._identifier: Optional[int] = None
        self._fan_on: Optional[bool] = None
        self._firmware_version: Optional[str] = None
        self._is_room_estimating: Optional[bool] = None
        self._connected: Optional[bool] = None
        self._last_connection: Optional[datetime] = None
        self._mode: Optional[str] = None
        self._power: Optional[int] = None
        self._predicted_room_temperature: Optional[float] = None
        self._room_id: Optional[int] = None
        self._thermostat_vendor: Optional[int] = None
        self._mqtt_username: Optional[str] = None
        self._mqtt_password: Optional[str] = None

        # Extra attributes not provided by API
        self._room_name: Optional[str] = None

        self._api = api

    @property
    def identifier(self) -> Optional[int]:  # pylint: disable=invalid-name
        """Return Fan id.

        This is a numerical ID generated by SmartCocoon
        This value can change if the fan is re-added to
        an cloud account
        """
        return self._identifier

    @property
    def fan_id(self) -> str:
        """Return Fan fan_id.

        This is the physical ID printed on the fan
        """
        return self._fan_id

    @property
    def fan_on(self) -> Optional[bool]:
        """Return a bool indicating if the fan is on or off
        On = True
        Off = False
        """
        return self._fan_on

    @property
    def firmware_version(self) -> Optional[str]:
        """Return the firmware version of the fan"""
        return self._firmware_version

    @property
    def is_room_estimating(self) -> Optional[bool]:
        """Return value to indicate if the room is estimating temp
        Estimating = True
        Not Estimating = False
        """
        return self._is_room_estimating

    @property
    def connected(self) -> Optional[bool]:
        """Return a bool indicating if the fan is connected
        Connected = True
        Not Connected = False
        """
        return self._connected

    @property
    def last_connection(self) -> Optional[datetime]:
        """Return a bool indicating if the fan is connected
        Connected = True
        Not Connected = False
        """
        return self._last_connection

    @property
    def mode(self) -> Optional[str]:
        """Return the current mode setting of the fan"""
        return self._mode

    @property
    def mode_enum(self) -> FanMode:
        """Return the current mode setting of the fan"""
        return FanMode(self._mode) if self._mode is not None else FanMode.OFF

    def get_extra_state_attributes(self) -> dict[str, Any]:
        """Return extra state attributes for Home Assistant integration."""
        attributes = {
            "mode": self._mode,
            "connected": self._connected,
            "last_connection": (
                self._last_connection.isoformat()
                if self._last_connection
                else None
            ),
            "firmware_version": self._firmware_version,
            "power": self._power,
            "room_name": self._room_name,
            "room_id": self._room_id,
        }

        # Add time since last connection
        if self._last_connection:
            time_since = (
                datetime.now(self._last_connection.tzinfo)
                - self._last_connection
            )
            attributes["time_since_connection"] = str(time_since).split(
                ".", maxsplit=1
            )[
                0
            ]  # Remove microseconds
            attributes["connection_status"] = (
                "Connected" if self._connected else "Disconnected"
            )
        else:
            attributes["time_since_connection"] = "Unknown"
            attributes["connection_status"] = "Unknown"

        return attributes

    @property
    def power(self) -> int:
        """Return the current power setting of the fan
        % value is multiplied by 100
        100% = power level of 10000
        """
        return self._power or 0

    @property
    def speed_pct(self) -> int:
        """Return the current power setting of the fan
        by %
        """
        return int(self._power / 100) if self._power is not None else 0

    @property
    def predicted_room_temperature(self) -> Optional[float]:
        """Return the predicted room temperature of the fan"""
        return self._predicted_room_temperature

    @property
    def room_name(self) -> Optional[str]:
        """Return the room name of the fan"""
        return self._room_name

    @property
    def room_id(self) -> Optional[int]:
        """Return the room id of the fan"""
        return self._room_id

    @property
    def thermostat_vendor(self) -> Optional[int]:
        """Return the thermostat vendor of the fan"""
        return self._thermostat_vendor

    @property
    def mqtt_username(self) -> Optional[str]:
        """Return the MQTT username of the fan"""
        return self._mqtt_username

    @property
    def mqtt_password(self) -> Optional[str]:
        """Return the MQTT password of the fan"""
        return self._mqtt_password

    def set_speed_pct(self, fan_speed_pct: int) -> bool:
        """Update the power of the fan"""

        if fan_speed_pct > 100:
            _LOGGER.debug(
                (
                    "Fan ID: %s - Fan speed of %s%% is invalid, must be "
                    "between 0%% and 100%%"
                ),
                self.fan_id,
                str(fan_speed_pct),
            )

        _LOGGER.debug(
            "Fan ID: %s - Updating fan speed to %s%%",
            self.fan_id,
            str(fan_speed_pct),
        )

        self._power = fan_speed_pct * 100
        return True

    def set_room_name(self, room_name: str) -> bool:
        """Update the room_name"""

        _LOGGER.debug(
            "Fan ID: %s - Updating room_name to '%s'", self.fan_id, room_name
        )

        self._room_name = room_name
        return True

    # pylint: disable=too-many-branches
    async def async_set_fan_modes(
        self,
        fan_mode: Optional[FanMode] = None,
        fan_speed_pct: Optional[int] = None,
    ) -> bool:
        """Set the fan mode and speed."""

        _LOGGER.debug(
            (
                "Fan ID: %s - In async_set_fan_mode with fan_mode: %s and"
                " fan_speed: %s%%"
            ),
            self.fan_id,
            fan_mode,
            str(fan_speed_pct),
        )

        if fan_mode is None and fan_speed_pct is None:
            _LOGGER.debug(
                "async_set_fan_modes must provide a value for fan_mode and/or"
                " fan_speed"
            )
            return False

        if fan_mode is None:
            fan_mode = derive_mode_from_speed(self.mode_enum, fan_speed_pct)

        # Update fan mode if changed
        if self.mode_enum != fan_mode:
            self._mode = fan_mode.value

        fan_speed_pct = resolve_speed(self.speed_pct, fan_mode, fan_speed_pct)
        if fan_speed_pct is None:
            return False

        # Update power if changed
        if self.speed_pct != fan_speed_pct:
            self.set_speed_pct(fan_speed_pct)

        # Attempt to update the fan via API
        success = await self._async_set_fan(fan_mode)
        return success

    # helpers moved to fan_helpers.py

    async def _async_set_fan(self, fan_mode: Optional[FanMode] = None) -> bool:
        """Call the API to update the fan mode and speed."""

        # Check if we have the required data to make the API call
        if self._identifier is None:
            _LOGGER.warning(
                "Fan ID: %s - Cannot update fan: identifier is None",
                self.fan_id,
            )
            return False

        if self.mode is None:
            _LOGGER.warning(
                "Fan ID: %s - Cannot update fan: mode is None", self.fan_id
            )
            return False

        # Make the API call
        await self._api.async_update_fan(
            fan_identifier=self._identifier,
            mode=self.mode,
            power=self.power,
        )

        _LOGGER.debug(
            "Fan ID: %s - Fan Mode was set to %s, speed to %s",
            self.fan_id,
            self.mode,
            self.speed_pct,
        )

        await self._async_update_fan()

        if fan_mode == FanMode.ON and not self.fan_on:
            _LOGGER.debug(
                "Fan ID: %s - Changing fan_on to 'True'", self.fan_id
            )
            self._fan_on = True
        elif fan_mode == FanMode.OFF:
            _LOGGER.debug(
                "Fan ID: %s - Changing fan_on to 'False'", self.fan_id
            )
            self._fan_on = False

        return True

    async def async_update_api_data(
        self,
        data: dict[str, Any],
    ) -> bool:
        """Selectively update the fan attributes with API data"""

        _LOGGER.debug("Fan ID: %s - In async_update_api_data", data["fan_id"])

        self._identifier = data["id"]
        self._fan_id = (
            data["fan_id"] if data.get("fan_id") is not None else self._fan_id
        )

        # Fan attributes from SmartCocoon
        self._mode = data["mode"]

        # fan_on does not always reflect the current mode, mode is more
        # accurate if set to always_on or always_off

        if self._mode == FanMode.ON.value:
            self._fan_on = True
        elif self._mode == FanMode.OFF.value:
            self._fan_on = False
        else:
            self._fan_on = data["fan_on"]

        self._firmware_version = data["firmware_version"]
        self._is_room_estimating = data["is_room_estimating"]
        # Log raw connected value from API for debugging
        raw_connected = data.get("connected")
        _LOGGER.debug(
            "Fan ID: %s - Raw API 'connected' value: %s (type: %s)",
            self.fan_id,
            raw_connected,
            type(raw_connected).__name__,
        )

        # Parse last_connection to datetime when provided as string
        last_conn = data["last_connection"]
        if isinstance(last_conn, str):
            try:
                iso_str = last_conn.replace("Z", "+00:00")
                self._last_connection = datetime.fromisoformat(iso_str)
            except Exception:  # pylint: disable=broad-except
                _LOGGER.debug(
                    "Fan ID: %s - Unable to parse last_connection: %s",
                    self.fan_id,
                    last_conn,
                )
                self._last_connection = None
        else:
            self._last_connection = last_conn

        # Correct stale connection status from API
        # If API says connected=True but last_connection is > 15 minutes ago,
        # the fan is actually disconnected (API has stale data)
        api_connected = data["connected"]
        if api_connected and self._last_connection:
            time_since_connection = (
                datetime.now(self._last_connection.tzinfo)
                - self._last_connection
            )
            minutes_since_connection = (
                time_since_connection.total_seconds() / 60
            )

            # If fan hasn't been seen in 15+ minutes, it's disconnected
            if minutes_since_connection > 15:
                _LOGGER.warning(
                    "Fan ID: %s - API reports connected=True but "
                    "last_connection was %.1f minutes ago. "
                    "Overriding to connected=False",
                    self.fan_id,
                    minutes_since_connection,
                )
                self._connected = False
            else:
                self._connected = api_connected
        elif api_connected and self._last_connection is None:
            # API says connected but no last_connection timestamp - trust API
            _LOGGER.debug(
                "Fan ID: %s - API reports connected=True but no "
                "last_connection timestamp available",
                self.fan_id,
            )
            self._connected = api_connected
        else:
            # API says connected=False, trust it
            self._connected = api_connected
        self._power = data["power"]
        self._predicted_room_temperature = data["predicted_room_temperature"]
        self._room_id = data["room_id"]
        self._thermostat_vendor = data["thermostat_vendor"]
        self._mqtt_username = data["mqtt_username"]
        self._mqtt_password = data["mqtt_password"]

        return True

    async def _async_update_fan(self) -> bool:
        _LOGGER.debug(
            "Fan ID: %s - Updating fan attributes from cloud", self.fan_id
        )

        if self._identifier is None:
            _LOGGER.warning(
                "Fan ID: %s - Cannot update fan: identifier is None",
                self.fan_id,
            )
            return False

        response = await self._api.async_get_fan(self._identifier)

        if response is None:
            _LOGGER.warning(
                "Fan ID: %s - Failed to get fan data from API", self.fan_id
            )
            return False

        await self.async_update_api_data(response)
        return True
