"""
Support for Dominos Pizza ordering.

The Dominos Pizza component creates a service which can be invoked to order
from their menu

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/dominos/.
"""
import logging
from datetime import timedelta

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components import http
from homeassistant.core import callback
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)

REQUIREMENTS = ['pizzapi==0.0.3']

DEPENDENCIES = ['http']

_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')