"""Shade data for the Hunter Douglas PowerView integration."""

from __future__ import annotations

from dataclasses import fields
from typing import Any

from aiopvapi.resources.model import PowerviewData
from aiopvapi.resources.shade import BaseShade, ShadePosition

from .util import async_map_data_by_id

POSITION_FIELDS = [field for field in fields(ShadePosition) if field.name != "velocity"]


def copy_position_data(source: ShadePosition, target: ShadePosition) -> ShadePosition:
    """Copy position data from source to target for None values only."""
    for field in POSITION_FIELDS:
        if (value := getattr(source, field.name)) is not None:
            setattr(target, field.name, value)


class PowerviewShadeData:
    """Coordinate shade data between multiple api calls."""

    def __init__(self) -> None:
        """Init the shade data."""
        self._raw_data_by_id: dict[int, dict[str | int, Any]] = {}
        self._shade_group_data_by_id: dict[int, BaseShade] = {}
        self.positions: dict[int, ShadePosition] = {}

    def get_raw_data(self, shade_id: int) -> dict[str | int, Any]:
        """Get data for the shade."""
        return self._raw_data_by_id[shade_id]

    def get_all_raw_data(self) -> dict[int, dict[str | int, Any]]:
        """Get data for all shades."""
        return self._raw_data_by_id

    def get_shade(self, shade_id: int) -> BaseShade:
        """Get specific shade from the coordinator."""
        return self._shade_group_data_by_id[shade_id]

    def get_shade_position(self, shade_id: int) -> ShadePosition:
        """Get positions for a shade."""
        if shade_id not in self.positions:
            shade_position = ShadePosition()
            # If we have the group data, use it to populate the initial position
            if shade := self._shade_group_data_by_id.get(shade_id):
                copy_position_data(shade.current_position, shade_position)
            self.positions[shade_id] = shade_position
        return self.positions[shade_id]

    def update_from_group_data(self, shade_id: int) -> None:
        """Process an update from the group data."""
        data = self._shade_group_data_by_id[shade_id]
        copy_position_data(data.current_position, self.get_shade_position(data.id))

    def store_group_data(self, shade_data: PowerviewData) -> None:
        """Store data from the all shades endpoint.

        This does not update the shades or positions (self.positions)
        as the data may be stale. update_from_group_data
        with a shade_id will update a specific shade
        from the group data.
        """
        self._shade_group_data_by_id = shade_data.processed
        self._raw_data_by_id = async_map_data_by_id(shade_data.raw)

    def update_shade_position(self, shade_id: int, new_position: ShadePosition) -> None:
        """Update a single shades position."""
        copy_position_data(new_position, self.get_shade_position(shade_id))

    def update_shade_velocity(self, shade_id: int, shade_data: ShadePosition) -> None:
        """Update a single shades velocity."""
        # the hub will always return a velocity of 0 on initial connect,
        # separate definition to store consistent value in HA
        # this value is purely driven from HA
        if shade_data.velocity is not None:
            self.get_shade_position(shade_id).velocity = shade_data.velocity
