"""Offer state listening automation rules."""
from __future__ import annotations

from datetime import timedelta
import logging
from typing import Any

import voluptuous as vol

from homeassistant import exceptions
from homeassistant.const import CONF_ATTRIBUTE, CONF_FOR, CONF_PLATFORM, MATCH_ALL
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, State, callback
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.event import (
    Event,
    async_track_same_state,
    async_track_state_change_event,
    process_state_match,
)

# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs
# mypy: no-check-untyped-defs

_LOGGER = logging.getLogger(__name__)

CONF_ENTITY_ID = "entity_id"
CONF_FROM = "from"
CONF_TO = "to"

BASE_SCHEMA = {
    vol.Required(CONF_PLATFORM): "state",
    vol.Required(CONF_ENTITY_ID): cv.entity_ids,
    vol.Optional(CONF_FOR): cv.positive_time_period_template,
    vol.Optional(CONF_ATTRIBUTE): cv.match_all,
}

TRIGGER_STATE_SCHEMA = vol.Schema(
    {
        **BASE_SCHEMA,
        # These are str on purpose. Want to catch YAML conversions
        vol.Optional(CONF_FROM): vol.Any(str, [str]),
        vol.Optional(CONF_TO): vol.Any(str, [str]),
    }
)

TRIGGER_ATTRIBUTE_SCHEMA = vol.Schema(
    {
        **BASE_SCHEMA,
        vol.Optional(CONF_FROM): cv.match_all,
        vol.Optional(CONF_TO): cv.match_all,
    }
)


def TRIGGER_SCHEMA(value: Any) -> dict:  # pylint: disable=invalid-name
    """Validate trigger."""
    if not isinstance(value, dict):
        raise vol.Invalid("Expected a dictionary")

    # We use this approach instead of vol.Any because
    # this gives better error messages.
    if CONF_ATTRIBUTE in value:
        return TRIGGER_ATTRIBUTE_SCHEMA(value)

    return TRIGGER_STATE_SCHEMA(value)


async def async_attach_trigger(
    hass: HomeAssistant,
    config,
    action,
    automation_info,
    *,
    platform_type: str = "state",
) -> CALLBACK_TYPE:
    """Listen for state changes based on configuration."""
    entity_id = config.get(CONF_ENTITY_ID)
    from_state = config.get(CONF_FROM, MATCH_ALL)
    to_state = config.get(CONF_TO, MATCH_ALL)
    time_delta = config.get(CONF_FOR)
    template.attach(hass, time_delta)
    match_all = from_state == MATCH_ALL and to_state == MATCH_ALL
    unsub_track_same = {}
    period: dict[str, timedelta] = {}
    match_from_state = process_state_match(from_state)