"""Data update coordinator for Subaru."""

from dataclasses import dataclass
from datetime import timedelta
import logging
import time
from typing import Any, override

from subarulink import Controller as SubaruAPI, SubaruException

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import (
    CONF_UPDATE_ENABLED,
    COORDINATOR_NAME,
    FETCH_INTERVAL,
    VEHICLE_LAST_UPDATE,
    VEHICLE_VIN,
)

_LOGGER = logging.getLogger(__name__)

type SubaruConfigEntry = ConfigEntry[SubaruRuntimeData]


@dataclass
class SubaruRuntimeData:
    """Runtime data for Subaru."""

    controller: SubaruAPI
    coordinator: SubaruDataUpdateCoordinator
    vehicles: dict[str, dict[str, Any]]


class SubaruDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
    """Class to manage fetching Subaru data."""

    config_entry: SubaruConfigEntry

    def __init__(
        self,
        hass: HomeAssistant,
        config_entry: SubaruConfigEntry,
        *,
        controller: SubaruAPI,
        vehicle_info: dict[str, dict[str, Any]],
    ) -> None:
        """Initialize the coordinator."""
        super().__init__(
            hass,
            _LOGGER,
            config_entry=config_entry,
            name=COORDINATOR_NAME,
            update_interval=timedelta(seconds=FETCH_INTERVAL),
        )
        self._controller = controller
        self._vehicle_info = vehicle_info

    @override
    async def _async_update_data(self) -> dict[str, Any]:
        """Fetch data from Subaru API."""
        try:
            return await _refresh_subaru_data(
                self.config_entry, self._vehicle_info, self._controller
            )
        except SubaruException as err:
            raise UpdateFailed(err.message) from err


async def _refresh_subaru_data(
    config_entry: ConfigEntry,
    vehicle_info: dict[str, dict[str, Any]],
    controller: SubaruAPI,
) -> dict[str, Any]:
    """Refresh local data with data fetched via Subaru API.

    Subaru API calls assume a server side vehicle context
    Data fetch/update must be done for each vehicle
    """
    data: dict[str, Any] = {}

    for vehicle in vehicle_info.values():
        vin = vehicle[VEHICLE_VIN]

        # Optionally send an "update" remote command to
        # vehicle (throttled with update_interval)
        if config_entry.options.get(CONF_UPDATE_ENABLED, False):
            await _update_subaru(vehicle, controller)

        # Fetch data from Subaru servers
        await controller.fetch(vin, force=True)

        # Update our local data that will go to entity states
        if received_data := await controller.get_data(vin):
            data[vin] = received_data

    return data


async def _update_subaru(vehicle: dict[str, Any], controller: SubaruAPI) -> None:
    """Commands remote vehicle update (polls the vehicle to update subaru API cache)."""
    cur_time = time.time()
    last_update = vehicle[VEHICLE_LAST_UPDATE]

    if cur_time - last_update > controller.get_update_interval():
        await controller.update(vehicle[VEHICLE_VIN], force=True)
        vehicle[VEHICLE_LAST_UPDATE] = cur_time
