from sqlalchemy import select, delete
from sqlalchemy.orm import selectinload, immediateload
from src.data.repositories.base import BaseRepository
from src.data.model.schemas import shopping_cart as schema
from src.data.model.db import partner_agent as db, article as article_db
from src.data.model.processing.price_conditions import apply_surcharge


class ShoppingCartRepository(BaseRepository):

    async def get_by_partner_id(self, partner_id: int, agent_id: int | None) -> schema.ShoppingCart:
        stmt = (select(db.ShoppingCart)
                .join(article_db.ArticleDetail)
                .options(selectinload(db.ShoppingCart.article))
                )
        if agent_id:
            stmt.where((db.ShoppingCart.agent_id == agent_id) & (db.ShoppingCart.partner_id == partner_id))
        else:
            stmt.where(db.ShoppingCart.partner_id == partner_id)

        query = await self.async_session.execute(stmt)
        items = query.scalars().all()

        shopping_cart_items = []
        shopping_cart_price = 0
        for item in items:
            overall_price = item.price * item.quantity
            shopping_cart_price += overall_price
            shopping_cart_items.append(
                schema.ShoppingCartItem(
                    id=item.id,
                    date_to=item.delivery_date_to.strftime("%Y-%m-%d"),
                    date_from=item.delivery_date_from.strftime("%Y-%m-%d"),
                    sales_order_type=item.sales_order_type_code,
                    article_number=item.article.article_number,
                    size=item.article.size,
                    color=item.article.color_code,
                    quantity=item.quantity,
                    price=item.price,
                    overall_price=overall_price,
                    delivery_number=item.partner_delivery_number
                )
            )
        result = schema.ShoppingCart(
            items=shopping_cart_items,
            price_sum=shopping_cart_price,
            shippingCost=0,
            hasShippingCost=False
        )

        return result

    async def remove(self, id: int):
        to_remove = await self.async_session.get(db.ShoppingCart, id)
        await self.async_session.delete(to_remove)
        await self.async_session.commit()
        return True

    async def add(self, new_item: schema.ShoppingCartItemInsert):
        id = await self.next_id(db.ShoppingCart.id)

        price = await self.get_price(new_item.sales_order_type, new_item.partner_id, new_item.ean)
        item = await self.async_session.get(db.ArticleDetail, new_item.ean, options=[
            selectinload(db.ArticleDetail.article)
        ])

        if new_item.sales_order_type != 201:
            partner = await self.async_session.get(db.Partner, new_item.partner_id)
            apply_surcharge(price, item.size, partner.surcharge_code)

        new = db.ShoppingCart(
            id=id,
            ean=new_item.ean,
            sales_order_type_code=new_item.sales_order_type,
            agent_id=new_item.agent_id,
            partner_id=new_item.partner_id,
            quantity=new_item.quantity,
            delivery_date_to=item.article.delivery_date_to,
            delivery_date_from=item.article.delivery_date_from,
            price=price
        )
        await self.async_session.merge(instance=new)
        await self.async_session.commit()

        return await self.db_to_schema(id)

    async def clear(self, partner_id: int) -> bool:
        """
        Clears shopping cart of selected partner
        :param partner_id:
        :return True on success, otherwise false
        """
        stmt = delete(db.ShoppingCart).where(db.ShoppingCart.partner_id == partner_id)
        await self.async_session.execute(stmt)
        await self.async_session.commit()
        return True

    async def db_to_schema(self, id: int):
        added = await self.async_session.get(db.ShoppingCart, id, options=[
            immediateload(db.ShoppingCart.article)
        ])
        return schema.ShoppingCartItem(
            id=id,
            article_number=added.article.article_number,
            size=added.article.size,
            color=added.article.color_code,
            quantity=added.quantity,
            price=added.price,
            date_to=added.delivery_date_to.strftime("%Y-%m-%d"),
            date_from=added.delivery_date_from.strftime("%Y-%m-%d"),
            delivery_number=added.partner_delivery_number,
            sales_order_type=added.sales_order_type_code,
            overall_price=added.price * added.quantity
        )

    async def check_cart(self, partner_id: int, agent_id: int):
        # sales order type -> minimal order amount per sales order type
        # todo extract this logic in processing
        def count_by_sales_order_type(sales_order_type: int):
            return len(list(filter(
                lambda i: i.sales_order_type_code == sales_order_type, items
            )))

        order_conditions = {
            140: 20, 120: 6, 100: 1, 110: 1, 299: 1
        }
        items = await self.__get_shopping_cart(partner_id, agent_id)
        a = count_by_sales_order_type(120)
        if order_conditions[120] >= a:
            return 1
        if (count_by_sales_order_type(120) / len(items)) >= 0.5:
            return 2
        c = count_by_sales_order_type(140)
        if order_conditions[140] >= c:
            return 3
        return 0

    async def count_cart_items(self, partner_id: int, agent_id: int):
        items = await self.__get_shopping_cart(partner_id, agent_id)
        return sum([
            item.quantity
            for item in items
        ])

    async def change_delivery_date(self, id: int, dates: schema.ShoppingCartItemChangeDate):
        for item in await self.__get_related_by_id(id):
            print(dates.date_to)
            if dates.date_to:
                item.delivery_date_to = dates.date_to
            if dates.date_from:
                item.delivery_date_from = dates.date_from
            await self.async_session.merge(instance=item)

        await self.async_session.commit()

        return True

    async def set_partner_delivery_number(self, id: int, number: str):
        for item in await self.__get_related_by_id(id):
            item.partner_delivery_number = number
            await self.async_session.merge(instance=item)

        await self.async_session.commit()
        return True

    async def __get_shopping_cart(self, partner_id: int, agent_id: int):
        stmt = (select(db.ShoppingCart)
                .where((db.ShoppingCart.partner_id == partner_id) & (db.ShoppingCart.agent_id == agent_id)))
        query = await self.async_session.scalars(stmt)
        return query.all()

    async def __get_related_by_id(self, id: int):
        ref_item = await self.async_session.get(db.ShoppingCart, id, options=[selectinload(db.ShoppingCart.article)])
        related_items = await self.async_session.scalars(
            select(db.ShoppingCart).join(db.ArticleDetail).where(
                (db.ShoppingCart.partner_id == ref_item.partner_id) &
                (db.ShoppingCart.agent_id == ref_item.agent_id) &
                (db.ShoppingCart.sales_order_type_code == ref_item.sales_order_type_code) &
                (db.ArticleDetail.color_code == ref_item.article.color_code)
            )
        )
        return related_items.all()
