import logging
from asyncio import sleep, create_task
from decimal import Decimal
from functools import partial
from typing import Optional, Callable

from rio import Column, Component, event, TextStyle, Text, Spacer, Revealer, ProgressCircle, ScrollContainer, Row, Popup, List, Rectangle, PointerEventListener, \
    PointerEvent, TextInput, TextInputChangeEvent, Color, Button, Checkbox, Switch

from src.ezgg_lan_manager import ConfigurationService, CateringService, AccountingService
from src.ezgg_lan_manager.components.CateringCartItem import CateringCartItem
from src.ezgg_lan_manager.components.CateringSelectionItem import CateringSelectionItem
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.services.CateringService import CateringError, CateringErrorType
from src.ezgg_lan_manager.types.CateringMenuItem import CateringMenuItemCategory, CateringMenuItem
from src.ezgg_lan_manager.types.CateringOrder import CateringMenuItemsWithAmount
from src.ezgg_lan_manager.types.UserSession import UserSession

POPUP_CLOSE_TIMEOUT_SECONDS = 3
logger = logging.getLogger(__name__.split(".")[-1])

class Cart(Component):
    cart: List[CateringMenuItem]
    user_id: Optional[int]
    clear_cb: Callable
    order_button_loading: bool = False
    popup_message: str = ""
    popup_is_shown: bool = False
    popup_is_error: bool = True
    print_receipt: bool = True

    async def on_remove_item(self, list_id: int) -> None:
        try:
            self.cart.pop(list_id)
        except IndexError:
            return

    async def on_empty_cart_pressed(self, _: PointerEvent) -> None:
        self.cart.clear()

    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, _: PointerEvent) -> None:
        if self.user_id is None:
            return
        self.order_button_loading = True
        self.force_refresh()

        show_popup_task = None
        if len(self.cart) < 1:
            show_popup_task = create_task(self.show_popup("Warenkorb leer", True))
        else:
            items_with_amounts: CateringMenuItemsWithAmount = {}
            for item in self.cart:
                try:
                    items_with_amounts[item] += 1
                except KeyError:
                    items_with_amounts[item] = 1
            try:
                await self.session[CateringService].place_order(items_with_amounts, self.user_id, is_delivery=False, print_receipt=self.print_receipt)
            except CateringError as catering_error:
                logger.error(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(f"Unbekannter Fehler: {catering_error}", True))
            else:
                self.cart.clear()
                self.user_id = None
                await self.clear_cb()
        self.order_button_loading = False
        if not show_popup_task:
            show_popup_task = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", False))

    def build(self) -> Component:
        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(self.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 self.cart), Decimal(0)))}",
                        style=TextStyle(
                            fill=self.session.theme.background_color,
                            font_size=0.8
                        ),
                        margin=1
                    ),
                    PointerEventListener(
                        content=Rectangle(
                            content=Text(
                                "Warenkorb leeren",
                                style=TextStyle(fill=self.session.theme.danger_color, font_size=0.9),
                                justify="center",
                                margin=0.2
                            ),
                            hover_fill=self.session.theme.hud_color,
                            transition_time=0.1,
                            margin=0.5,
                            cursor="pointer"
                        ),
                        on_press=self.on_empty_cart_pressed
                    ),
                    PointerEventListener(
                        content=Rectangle(
                            content=Text(
                                "Bestellen",
                                style=TextStyle(fill=self.session.theme.success_color, font_size=0.9),
                                justify="center",
                                margin=0.2
                            ),
                            hover_fill=self.session.theme.hud_color if self.user_id is not None else self.session.theme.danger_color,
                            transition_time=0.1,
                            margin=0.5,
                            cursor="pointer" if self.user_id is not None else "not-allowed"
                        ),
                        on_press=self.on_order_pressed
                    )
                )
        )


class NewPosOrderPage(Component):
    user_id_input_value: str = ""
    user_id: Optional[int] = None
    all_menu_items: Optional[list[CateringMenuItem]] = None
    cart: List[CateringMenuItem] = List()
    print_receipt: bool = True

    @event.on_populate
    async def on_populate(self) -> None:
        await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Neue Bestellung anlegen")
        self.all_menu_items = await self.session[CateringService].get_menu()

    async def on_user_logged_in_status_changed(self) -> None:
        self.force_refresh()

    async def on_user_id_input_change(self, change_event: TextInputChangeEvent) -> None:
        try:
            id_ = int(change_event.text)
        except ValueError:
            return

        self.user_id = id_
        
    async def on_add(self, article_id: int) -> None:
        try:
            menu_item = await self.session[CateringService].get_menu_item_by_id(article_id)
        except CateringError as e:
            logger.error(e)
            return
        self.cart.append(menu_item)

    @staticmethod
    def get_menu_items_by_category(all_menu_items: list[CateringMenuItem], category: Optional[CateringMenuItemCategory]) -> list[CateringMenuItem]:
        return list(filter(lambda item: item.category == category, all_menu_items))

    async def clear_user_id_input(self) -> None:
        self.user_id_input_value = ""

    def build(self) -> Component:
        try:
            is_team_member = self.session[UserSession].is_team_member
        except KeyError:
            is_team_member = False


        shopping_cart_container = MainViewContentBox(
            Column(
                Text(
                    text="Neue Bestellung anlegen",
                    style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin_top=2,
                    margin_bottom=0.5,
                    align_x=0.5
                ),
                Row(
                    TextInput(text=self.bind().user_id_input_value, label="Nutzer ID", on_change=self.on_user_id_input_change, change_delay=1, margin_bottom=0.5, margin_left=1, margin_right=1, min_width=20),
                    Text("Bon drucken?:", margin_right=0, margin_left=0, style=TextStyle(fill=self.session.theme.background_color)),
                    Switch(is_on=self.bind().print_receipt, margin_right=1)
                ),
                Cart(cart=self.cart, user_id=self.user_id, clear_cb=self.clear_user_id_input, print_receipt=self.print_receipt)
            )
        ) if is_team_member else Spacer()

        menu = [MainViewContentBox(
            ProgressCircle(
                color="secondary",
                align_x=0.5,
                margin_top=2,
                margin_bottom=2
            )
        )] if not self.all_menu_items else [MainViewContentBox(
            Revealer(
                header="Snacks",
                content=Column(
                    *[CateringSelectionItem(
                        article_name=catering_menu_item.name,
                        article_price=catering_menu_item.price,
                        article_id=catering_menu_item.item_id,
                        on_add_callback=self.on_add,
                        is_sensitive=not catering_menu_item.is_disabled,
                        additional_info=catering_menu_item.additional_info,
                        is_grey=idx % 2 == 0
                    ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.SNACK))],
                ),
                header_style=TextStyle(
                    fill=self.session.theme.background_color,
                    font_size=1.2
                ),
                margin=1,
                align_y=0.5
            )
        ),
            MainViewContentBox(
                Revealer(
                    header="Frühstück",
                    content=Column(
                        *[CateringSelectionItem(
                            article_name=catering_menu_item.name,
                            article_price=catering_menu_item.price,
                            article_id=catering_menu_item.item_id,
                            on_add_callback=self.on_add,
                            is_sensitive=not catering_menu_item.is_disabled,
                            additional_info=catering_menu_item.additional_info,
                            is_grey=idx % 2 == 0
                        ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BREAKFAST))],
                    ),
                    header_style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin=1,
                    align_y=0.5
                )
            ),
            MainViewContentBox(
                Revealer(
                    header="Hauptspeisen",
                    content=Column(
                        *[CateringSelectionItem(
                            article_name=catering_menu_item.name,
                            article_price=catering_menu_item.price,
                            article_id=catering_menu_item.item_id,
                            on_add_callback=self.on_add,
                            is_sensitive=not catering_menu_item.is_disabled,
                            additional_info=catering_menu_item.additional_info,
                            is_grey=idx % 2 == 0
                        ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.MAIN_COURSE))],
                    ),
                    header_style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin=1,
                    align_y=0.5
                )
            ),
            MainViewContentBox(
                Revealer(
                    header="Desserts",
                    content=Column(
                        *[CateringSelectionItem(
                            article_name=catering_menu_item.name,
                            article_price=catering_menu_item.price,
                            article_id=catering_menu_item.item_id,
                            on_add_callback=self.on_add,
                            is_sensitive=not catering_menu_item.is_disabled,
                            additional_info=catering_menu_item.additional_info,
                            is_grey=idx % 2 == 0
                        ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.DESSERT))],
                    ),
                    header_style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin=1,
                    align_y=0.5
                )
            ),
            MainViewContentBox(
                Revealer(
                    header="Wasser & Softdrinks",
                    content=Column(
                        *[CateringSelectionItem(
                            article_name=catering_menu_item.name,
                            article_price=catering_menu_item.price,
                            article_id=catering_menu_item.item_id,
                            on_add_callback=self.on_add,
                            is_sensitive=not catering_menu_item.is_disabled,
                            additional_info=catering_menu_item.additional_info,
                            is_grey=idx % 2 == 0
                        ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC))],
                    ),
                    header_style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin=1,
                    align_y=0.5
                )
            ),
            MainViewContentBox(
                Revealer(
                    header="Alkoholische Getränke",
                    content=Column(
                        *[CateringSelectionItem(
                            article_name=catering_menu_item.name,
                            article_price=catering_menu_item.price,
                            article_id=catering_menu_item.item_id,
                            on_add_callback=self.on_add,
                            is_sensitive=not catering_menu_item.is_disabled,
                            additional_info=catering_menu_item.additional_info,
                            is_grey=idx % 2 == 0
                        ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BEVERAGE_ALCOHOLIC))],
                    ),
                    header_style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin=1,
                    align_y=0.5
                )
            ),
            MainViewContentBox(
                Revealer(
                    header="Cocktails & Longdrinks",
                    content=Column(
                        *[CateringSelectionItem(
                            article_name=catering_menu_item.name,
                            article_price=catering_menu_item.price,
                            article_id=catering_menu_item.item_id,
                            on_add_callback=self.on_add,
                            is_sensitive=not catering_menu_item.is_disabled,
                            additional_info=catering_menu_item.additional_info,
                            is_grey=idx % 2 == 0
                        ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BEVERAGE_COCKTAIL))],
                    ),
                    header_style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin=1,
                    align_y=0.5
                )
            ),
            MainViewContentBox(
                Revealer(
                    header="Shots",
                    content=Column(
                        *[CateringSelectionItem(
                            article_name=catering_menu_item.name,
                            article_price=catering_menu_item.price,
                            article_id=catering_menu_item.item_id,
                            on_add_callback=self.on_add,
                            is_sensitive=not catering_menu_item.is_disabled,
                            additional_info=catering_menu_item.additional_info,
                            is_grey=idx % 2 == 0
                        ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BEVERAGE_SHOT))],
                    ),
                    header_style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin=1,
                    align_y=0.5
                )
            ),
            MainViewContentBox(
                Revealer(
                    header="Sonstiges",
                    content=Column(
                        *[CateringSelectionItem(
                            article_name=catering_menu_item.name,
                            article_price=catering_menu_item.price,
                            article_id=catering_menu_item.item_id,
                            on_add_callback=self.on_add,
                            is_sensitive=not catering_menu_item.is_disabled,
                            additional_info=catering_menu_item.additional_info,
                            is_grey=idx % 2 == 0
                        ) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.NON_FOOD))],
                    ),
                    header_style=TextStyle(
                        fill=self.session.theme.background_color,
                        font_size=1.2
                    ),
                    margin=1,
                    align_y=0.5
                )
            )]

        return Column(shopping_cart_container, *menu, align_y=0)
