from asyncio import sleep, create_task
from decimal import Decimal
from typing import Optional

from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table, event, Card

from src.ezgg_lan_manager.components.CateringCartItem import CateringCartItem
from src.ezgg_lan_manager.components.CateringOrderItem import CateringOrderItem
from src.ezgg_lan_manager.services.AccountingService import AccountingService
from src.ezgg_lan_manager.services.CateringService import CateringService, CateringError, CateringErrorType
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder, CateringMenuItemsWithAmount
from src.ezgg_lan_manager.types.UserSession import UserSession

POPUP_CLOSE_TIMEOUT_SECONDS = 3

class ShoppingCartAndOrders(Component):
    show_cart: bool = True
    orders: list[CateringOrder] = []
    order_button_loading: bool = False
    popup_message: str = ""
    popup_is_shown: bool = False
    popup_is_error: bool = True

    @event.periodic(5)
    async def periodic_refresh_of_orders(self) -> None:
        user_id = self._get_user_id()
        if not self.show_cart and not self.popup_is_shown and user_id is not None:
            self.orders = await self.session[CateringService].get_orders_for_user(user_id)

    async def switch(self) -> None:
        self.show_cart = not self.show_cart
        user_id = self._get_user_id()
        if user_id is not None:
            self.orders = await self.session[CateringService].get_orders_for_user(user_id)

    async def on_remove_item(self, list_id: int) -> None:
        catering_service = self.session[CateringService]
        user_id = self._get_user_id()
        if user_id is None:
            return
        cart = catering_service.get_cart(user_id)
        try:
            cart.pop(list_id)
        except IndexError:
            return
        catering_service.save_cart(user_id, cart)
        self.force_refresh()

    async def on_empty_cart_pressed(self) -> None:
        user_id = self._get_user_id()
        if user_id is None:
            return
        self.session[CateringService].save_cart(user_id, [])
        self.force_refresh()

    async def on_add_item(self, article_id: int) -> None:
        catering_service = self.session[CateringService]
        user_id = self._get_user_id()
        if user_id is None:
            return
        cart = catering_service.get_cart(user_id)
        item_to_add = await catering_service.get_menu_item_by_id(article_id)
        cart.append(item_to_add)
        catering_service.save_cart(user_id, cart)
        self.force_refresh()

    async def show_popup(self, text: str, is_error: bool) -> None:
        self.popup_is_error = is_error
        self.popup_message = text
        self.popup_is_shown = True
        self.force_refresh()
        await sleep(POPUP_CLOSE_TIMEOUT_SECONDS)
        self.popup_is_shown = False
        self.force_refresh()

    async def on_order_pressed(self) -> None:
        self.order_button_loading = True
        self.force_refresh()

        user_id = self._get_user_id()
        if user_id is None:
            return
        cart = self.session[CateringService].get_cart(user_id)
        show_popup_task = None
        if len(cart) < 1:
            show_popup_task = create_task(self.show_popup("Warenkorb leer", True))
        else:
            items_with_amounts: CateringMenuItemsWithAmount = {}
            for item in cart:
                try:
                    items_with_amounts[item] += 1
                except KeyError:
                    items_with_amounts[item] = 1
            try:
                await self.session[CateringService].place_order(items_with_amounts, user_id)
            except CateringError as catering_error:
                if catering_error.error_type == CateringErrorType.INCLUDES_DISABLED_ITEM:
                    show_popup_task = create_task(self.show_popup("Warenkorb enthält gesperrte Artikel", True))
                elif catering_error.error_type == CateringErrorType.INSUFFICIENT_FUNDS:
                    show_popup_task = create_task(self.show_popup("Guthaben nicht ausreichend", True))
                else:
                    show_popup_task = create_task(self.show_popup("Unbekannter Fehler", True))
            else:
                self.session[CateringService].save_cart(user_id, [])
        self.order_button_loading = False
        if not show_popup_task:
            show_popup_task = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", False))

    async def _create_order_info_modal(self, order: CateringOrder) -> None:
        def build_dialog_content() -> Component:
            # @todo: rio 0.10.8 did not have the ability to align the columns, check back in a future version
            table = Table(
                        {
                            "Artikel": [item.name for item in order.items.keys()] + ["Gesamtpreis:"],
                            "Anzahl": [item for item in order.items.values()] + [""],
                            "Preis": [AccountingService.make_euro_string_from_decimal(item.price) for item in order.items.keys()] + [AccountingService.make_euro_string_from_decimal(order.price)],
                        },
                        show_row_numbers=False
                    )
            return Card(
                Column(
                    Text(
                        f"Deine Bestellung ({order.order_id})",
                        align_x=0.5,
                        margin_bottom=0.5
                    ),
                    table,
                    margin=2,
                ),
                align_x=0.5,
                align_y=0.2,
                min_width=50,
                min_height=10,
                color=self.session.theme.primary_color,
                margin_left=1,
                margin_right=1,
                margin_top=2,
                margin_bottom=1,
            )
        dialog = await self.session.show_custom_dialog(
            build=build_dialog_content,
            modal=True,
            user_closable=True,
        )
        await dialog.wait_for_close()

    def _get_user_id(self) -> Optional[int]:
        try:
            return self.session[UserSession].user_id
        except KeyError:
            return None

    def build(self) -> Component:
        user_id = self._get_user_id()
        catering_service = self.session[CateringService]
        cart = catering_service.get_cart(user_id) if user_id is not None else []
        if self.show_cart:
            cart_container = ScrollContainer(
                content=Column(
                    *[CateringCartItem(
                        article_name=cart_item.name,
                        article_price=cart_item.price,
                        article_id=cart_item.item_id,
                        remove_item_cb=self.on_remove_item,
                        list_id=idx
                    ) for idx, cart_item in enumerate(cart)],
                    Spacer(grow_y=True)
                ),
                min_height=8,
                min_width=33,
                margin=1
            )
            return Column(
                    Popup(
                        anchor=cart_container,
                        content=Text(self.popup_message, style=TextStyle(fill=self.session.theme.danger_color if self.popup_is_error else self.session.theme.success_color), overflow="wrap", margin=2, justify="center", min_width=20),
                        is_open=self.popup_is_shown,
                        position="center",
                        color=self.session.theme.primary_color
                    ),
                    Row(
                        Text(
                            text=f"Preis: {AccountingService.make_euro_string_from_decimal(sum((cart_item.price for cart_item in cart), Decimal(0)))}",
                            style=TextStyle(
                                fill=self.session.theme.background_color,
                                font_size=0.8
                            ),
                            margin=1
                        ),
                        Button(
                            content=Text(
                                "Warenkorb leeren",
                                style=TextStyle(fill=self.session.theme.danger_color, font_size=0.9),
                                align_x=0.2
                            ),
                            margin=1,
                            margin_left=0,
                            shape="rectangle",
                            style="major",
                            color="primary",
                            on_press=self.on_empty_cart_pressed
                        ),
                        Button(
                            content=Text(
                                "Bestellen",
                                style=TextStyle(fill=self.session.theme.success_color, font_size=0.9),
                                align_x=0.2
                            ),
                            margin=1,
                            margin_left=0,
                            shape="rectangle",
                            style="major",
                            color="primary",
                            on_press=self.on_order_pressed,
                            is_loading=self.order_button_loading
                        )
                    )
            )
        else:
            orders_container = ScrollContainer(
                content=Column(
                    *[CateringOrderItem(
                        order=order_item,
                        info_modal_cb=self._create_order_info_modal
                    ) for order_item in self.orders],
                    Spacer(grow_y=True)
                ),
                min_height=8,
                min_width=33,
                margin=1
            )
            return Column(orders_container)
