"""Support for Dominos Pizza ordering."""
from datetime import timedelta
import logging

import voluptuous as vol

from homeassistant.components import http
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import Throttle

_LOGGER = logging.getLogger(__name__)

# The domain of your component. Should be equal to the name of your component.
DOMAIN = "dominos"
ENTITY_ID_FORMAT = DOMAIN + ".{}"

ATTR_COUNTRY = "country_code"
ATTR_FIRST_NAME = "first_name"
ATTR_LAST_NAME = "last_name"
ATTR_EMAIL = "email"
ATTR_PHONE = "phone"
ATTR_ADDRESS = "address"
ATTR_ORDERS = "orders"
ATTR_SHOW_MENU = "show_menu"
ATTR_ORDER_ENTITY = "order_entity_id"
ATTR_ORDER_NAME = "name"
ATTR_ORDER_CODES = "codes"

MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
MIN_TIME_BETWEEN_STORE_UPDATES = timedelta(minutes=3330)

_ORDERS_SCHEMA = vol.Schema(
    {
        vol.Required(ATTR_ORDER_NAME): cv.string,
        vol.Required(ATTR_ORDER_CODES): vol.All(cv.ensure_list, [cv.string]),
    }
)

CONFIG_SCHEMA = vol.Schema(
    {
        DOMAIN: vol.Schema(
            {
                vol.Required(ATTR_COUNTRY): cv.string,
                vol.Required(ATTR_FIRST_NAME): cv.string,
                vol.Required(ATTR_LAST_NAME): cv.string,
                vol.Required(ATTR_EMAIL): cv.string,
                vol.Required(ATTR_PHONE): cv.string,
                vol.Required(ATTR_ADDRESS): cv.string,
                vol.Optional(ATTR_SHOW_MENU): cv.boolean,
                vol.Optional(ATTR_ORDERS, default=[]): vol.All(
                    cv.ensure_list, [_ORDERS_SCHEMA]
                ),
            }
        )
    },
    extra=vol.ALLOW_EXTRA,
)


def setup(hass, config):
    """Set up is called when Home Assistant is loading our component."""
    dominos = Dominos(hass, config)

    component = EntityComponent(_LOGGER, DOMAIN, hass)
    hass.data[DOMAIN] = {}
    entities = []
    conf = config[DOMAIN]

    hass.services.register(DOMAIN, "order", dominos.handle_order)

    if conf.get(ATTR_SHOW_MENU):
        hass.http.register_view(DominosProductListView(dominos))

    for order_info in conf.get(ATTR_ORDERS):
        order = DominosOrder(order_info, dominos)
        entities.append(order)

    if entities:
        component.add_entities(entities)

    # Return boolean to indicate that initialization was successfully.
    return True


class Dominos:
    """Main Dominos service."""

    def __init__(self, hass, config):
        """Set up main service."""
        conf = config[DOMAIN]
        from pizzapi import Address, Customer
        from pizzapi.address import StoreException

        self.hass = hass
        self.customer = Customer(
            conf.get(ATTR_FIRST_NAME),
            conf.get(ATTR_LAST_NAME),
            conf.get(ATTR_EMAIL),
            conf.get(ATTR_PHONE),
            conf.get(ATTR_ADDRESS),
        )
        self.address = Address(
            *self.customer.address.split(","), country=conf.get(ATTR_COUNTRY)
        )
        self.country = conf.get(ATTR_COUNTRY)
        try:
            self.closest_store = self.address.closest_store()
        except StoreException:
            self.closest_store = None

    def handle_order(self, call):
        """Handle ordering pizza."""
        entity_ids = call.data.get(ATTR_ORDER_ENTITY, None)

        target_orders = [
            order
            for order in self.hass.data[DOMAIN]["entities"]
            if order.entity_id in entity_ids
        ]

        for order in target_orders:
            order.place()

    @Throttle(MIN_TIME_BETWEEN_STORE_UPDATES)
    def update_closest_store(self):
        """Update the shared closest store (if open)."""
        from pizzapi.address import StoreException

        try:
            self.closest_store = self.address.closest_store()
            return True
        except StoreException:
            self.closest_store = None
            return False

    def get_menu(self):
        """Return the products from the closest stores menu."""
        self.update_closest_store()
        if self.closest_store is None:
            _LOGGER.warning("Cannot get menu. Store may be closed")
            return []
        menu = self.closest_store.get_menu()
        product_entries = []

        for product in menu.products:
            item = {}
            if isinstance(product.menu_data["Variants"], list):
                variants = ", ".join(product.menu_data["Variants"])
            else:
                variants = product.menu_data["Variants"]
            item["name"] = product.name
            item["variants"] = variants
            product_entries.append(item)

        return product_entries


class DominosProductListView(http.HomeAssistantView):
    """View to retrieve product list content."""

    url = "/api/dominos"
    name = "api:dominos"

    def __init__(self, dominos):
        """Initialize suite view."""
        self.dominos = dominos

    @callback
    def get(self, request):
        """Retrieve if API is running."""
        return self.json(self.dominos.get_menu())


class DominosOrder(Entity):
    """Represents a Dominos order entity."""

    def __init__(self, order_info, dominos):
        """Set up the entity."""
        self._name = order_info["name"]
        self._product_codes = order_info["codes"]
        self._orderable = False
        self.dominos = dominos

    @property
    def name(self):
        """Return the orders name."""
        return self._name

    @property
    def product_codes(self):
        """Return the orders product codes."""
        return self._product_codes

    @property
    def orderable(self):
        """Return the true if orderable."""
        return self._orderable

    @property
    def state(self):
        """Return the state either closed, orderable or unorderable."""
        if self.dominos.closest_store is None:
            return "closed"
        return "orderable" if self._orderable else "unorderable"

    @Throttle(MIN_TIME_BETWEEN_UPDATES)
    def update(self):
        """Update the order state and refreshes the store."""
        from pizzapi.address import StoreException

        try:
            self.dominos.update_closest_store()
        except StoreException:
            self._orderable = False
            return

        try:
            order = self.order()
            order.pay_with()
            self._orderable = True
        except StoreException:
            self._orderable = False

    def order(self):
        """Create the order object."""
        from pizzapi import Order
        from pizzapi.address import StoreException

        if self.dominos.closest_store is None:
            raise StoreException

        order = Order(
            self.dominos.closest_store,
            self.dominos.customer,
            self.dominos.address,
            self.dominos.country,
        )

        for code in self._product_codes:
            order.add_item(code)

        return order

    def place(self):
        """Place the order."""
        from pizzapi.address import StoreException

        try:
            order = self.order()
            order.place()
        except StoreException:
            self._orderable = False
            _LOGGER.warning(
                "Attempted to order Dominos - Order invalid or store closed"
            )
