"""
surepy.devices
====================================
ABC representing a Sure Petcare Device.

|license-info|
"""

from __future__ import annotations

import logging

from abc import ABC
from typing import Any
from urllib.parse import urlparse

from surepy.const import SURE_BATT_VOLTAGE_FULL, SURE_BATT_VOLTAGE_LOW
from surepy.entities import SurepyEntity
from surepy.enums import BowlPosition, FoodType, LockState


# get a logger
logger: logging.Logger = logging.getLogger(__name__)


class Hub(SurepyEntity):
    """Sure Petcare Hub."""

    @property
    def online(self) -> bool:
        return bool(self._data.get("status", {}).get("online"))

    @property
    def parent_id(self) -> int | None:
        return self._data.get("parent_device_id", None)

    @property
    def serial(self) -> str | None:
        """ID of the household the pet belongs to."""
        return str(serial) if (serial := self._data.get("serial_number")) else None

    @property
    def icon(self) -> str | None:
        """Picture of the Pet."""
        return urlparse("https://surehub.io/assets/images/hub-icon.svg").geturl()


class SurepyDevice(SurepyEntity, ABC):
    """Abstract Surepy base device"""

    @property
    def parent_id(self) -> int | None:
        return self._data.get("parent_device_id", None)

    @property
    def serial(self) -> str | None:
        """ID of the household the pet belongs to."""
        return str(serial) if (serial := self._data.get("serial_number")) else None

    @property
    def battery_level(self) -> int | None:
        """Return battery level in percent."""
        return self.calculate_battery_level()

    def calculate_battery_level(
        self,
        voltage_full: float = SURE_BATT_VOLTAGE_FULL,
        voltage_low: float = SURE_BATT_VOLTAGE_LOW,
        num_batteries: int = 4,
    ) -> int | None:
        """Return battery voltage."""

        try:
            voltage_diff = voltage_full - voltage_low
            battery_voltage = float(self._data["status"]["battery"])
            voltage_per_battery = battery_voltage / num_batteries
            voltage_per_battery_diff = voltage_per_battery - voltage_low

            # return batterie level between 0 and 100
            return max(min(int(voltage_per_battery_diff / voltage_diff * 100), 100), 0)

        except (KeyError, TypeError, ValueError) as error:
            logger.debug("error while calculating battery level: %s", error)
            return None


class FeederBowl:
    """Sure Petcare Felaqua."""

    def __init__(self, data: dict[str, int | float | str], feeder: Feeder):
        """Initialize a Sure Petcare sensor."""

        self._data: dict[str, int | float | str] = data
        self._name = f"{feeder.name} Bowl {self._data['index']}"

    @property
    def name(self) -> str:
        return self._name

    @property
    def weight(self) -> float:
        return float(self._data["weight"])

    @property
    def change(self) -> float:
        return float(self._data["change"])

    @property
    def target(self) -> int | None:
        return int(self._data["target"]) if "target" in self._data else None

    @property
    def index(self) -> int | None:
        return int(self._data["index"]) if "index" in self._data else None

    @property
    def food_type_id(self) -> int | None:
        return int(self._data["food_type_id"]) if "food_type_id" in self._data else None

    @property
    def food_type(self) -> str | None:
        return FoodType(self.food_type_id).name.capitalize() if self.food_type_id else None

    @property
    def position(self) -> str | None:
        return BowlPosition(self.index).name.capitalize() if self.index else None

    def raw_data(self) -> dict[str, int | float | str]:
        return self._data

class Tag:
    """Tags assigned to a device."""

    def __init__(self, data: dict[str, int | float | str], feeder: Feeder):
        """Initialize a Sure Petcare sensor."""

        self._data: dict[str, int | float | str] = data

    @property
    def id(self) -> int:
        return int(self._data["id"])

    def index(self) -> int:
        return int(self._data["index"])

    def profile(self) -> int:
        return int(self._data["profile"])

    def version(self) -> str:
        return self._data["version"]

    def created_at(self) -> str:
        return str(self._data["created_at"])

    def updated_at(self) -> str:
        return self._data["updated_at"]

    def raw_data(self) -> dict[str, int | float | str]:
        return self._data

class Feeder(SurepyDevice):
    """Sure Petcare Cat- or Pet-Flap."""

    def __init__(self, data: dict[str, Any]):
        """Initialize a Sure Petcare sensor."""
        super().__init__(data)

        self.bowls: dict[int, FeederBowl] = {}

        self.add_bowls()

        self.tags: dict[int, Tag] = {}

        self.add_tags()

    @property
    def bowl_count(self) -> int:
        return len(self.bowls)

    @property
    def total_weight(self) -> float:
        return sum([bowl.weight or 0.0 for bowl in self.bowls.values() if bowl.weight > 0.0])

    def add_bowls(self) -> None:
        if lunch := self._data.get("lunch"):
            for bowl in lunch.get("weights", []):
                self.bowls[bowl["index"]] = FeederBowl(data=bowl, feeder=self)

    @property
    def icon(self) -> str | None:
        """Icon of the Felaqua."""
        return urlparse("https://surehub.io/assets/images/feeder-left-menu.png").geturl()

    def add_tags(self) -> None:
        if tags := self._data.get("tags"):
            for tag in tags:
                self.tags[tag["index"]] = Tag(data=tag, feeder=self)

class Felaqua(SurepyDevice):
    """Sure Petcare Cat- or Pet-Flap."""

    @property
    def water_remaining(self) -> float | None:
        remaining = None

        try:
            remaining = float(self._data["latest_drink"]["remaining"])
        except (KeyError, TypeError):
            pass

        return remaining

    @property
    def water_change(self) -> float | None:
        change = None

        try:
            change = float(self._data["latest_drink"]["change"])
        except (KeyError, TypeError):
            pass

        return change

    @property
    def icon(self) -> str | None:
        """Icon of the Felaqua."""
        return urlparse("https://surehub.io/assets/images/poseidon-left-menu.png").geturl()


class Flap(SurepyDevice):
    """Sure Petcare Cat- or Pet-Flap."""

    @property
    def state(self) -> LockState:
        return LockState(self._data["status"]["locking"]["mode"])

    @property
    def unlocked(self) -> bool:
        return self.state in [LockState.UNLOCKED, LockState.CURFEW_UNLOCKED]

    @property
    def icon(self) -> str | None:
        """Icon of the Pet/Cap Flap."""

        icon_url = "https://surehub.io/assets/images/petdoor-left-menu.png"

        if self.state == LockState.LOCKED_ALL:
            icon_url = "https://surehub.io/assets/images/both-ways-icon.svg"
        elif self.state == LockState.LOCKED_IN:
            icon_url = "https://surehub.io/assets/images/inside-icon.svg"
        elif self.state == LockState.LOCKED_OUT:
            icon_url = "https://surehub.io/assets/images/outside-icon.svg"

        return urlparse(icon_url).geturl()
