from typing import Dict, List

from marshmallow import fields, validate

from maps_adv.common.protomallow import PbDateTimeField, PbEnumField, ProtobufSchema
from maps_adv.manul.lib.data_managers import OrdersDataManager
from maps_adv.manul.lib.db.enums import CurrencyType, RateType
from maps_adv.manul.proto import orders_pb2

from .schema_utils import with_schemas

enums_map = {
    "currency_type": [
        (orders_pb2.CurrencyType.Value("RUB"), CurrencyType.RUB),
        (orders_pb2.CurrencyType.Value("BYN"), CurrencyType.BYN),
        (orders_pb2.CurrencyType.Value("TRY"), CurrencyType.TRY),
        (orders_pb2.CurrencyType.Value("KZT"), CurrencyType.KZT),
        (orders_pb2.CurrencyType.Value("EUR"), CurrencyType.EUR),
        (orders_pb2.CurrencyType.Value("USD"), CurrencyType.USD),
    ],
    "rate_type": [
        (orders_pb2.RateType.Value("FREE"), RateType.FREE),
        (orders_pb2.RateType.Value("PAID"), RateType.PAID),
    ],
}


class OrderInputSchema(ProtobufSchema):
    class Meta:
        pb_message_class = orders_pb2.OrderInput

    title = fields.String(validate=validate.Length(min=1, max=256))
    client_id = fields.Integer()
    product_id = fields.Integer()
    currency = PbEnumField(
        enum=CurrencyType,
        pb_enum=orders_pb2.CurrencyType,
        values_map=enums_map["currency_type"],
        by_value=True,
    )
    comment = fields.String(validate=validate.Length(max=1024), missing="")
    rate = PbEnumField(
        enum=RateType,
        pb_enum=orders_pb2.RateType,
        values_map=enums_map["rate_type"],
        by_value=True,
        missing=orders_pb2.RateType.Value("PAID"),
    )


class OrderOutputSchema(ProtobufSchema):
    class Meta:
        pb_message_class = orders_pb2.OrderOutput

    id = fields.Integer()
    title = fields.String(validate=validate.Length(min=1, max=256))
    created_at = PbDateTimeField()
    client_id = fields.Integer()
    product_id = fields.Integer()
    currency = PbEnumField(
        enum=CurrencyType,
        pb_enum=orders_pb2.CurrencyType,
        values_map=enums_map["currency_type"],
        by_value=True,
    )
    comment = fields.String(validate=validate.Length(max=1024), missing="")
    rate = PbEnumField(
        enum=RateType,
        pb_enum=orders_pb2.RateType,
        values_map=enums_map["rate_type"],
        by_value=True,
    )


class OrdersListSchema(ProtobufSchema):
    class Meta:
        pb_message_class = orders_pb2.OrdersList

    orders = fields.List(fields.Nested(OrderOutputSchema))


class OrdersFilterSchema(ProtobufSchema):
    class Meta:
        pb_message_class = orders_pb2.OrdersFilter

    ids = fields.List(fields.Integer())


class AccountManagerIdFilterSchema(ProtobufSchema):
    class Meta:
        pb_message_class = orders_pb2.AccountManagerIdFilter

    account_manager_id = fields.Integer()


class OrdersApiProvider:
    __slots__ = ("_dm",)

    _dm: OrdersDataManager

    def __init__(self, dm: OrdersDataManager):
        self._dm = dm

    @with_schemas(OrderInputSchema, OrderOutputSchema)
    async def create_order(self, **kwargs) -> dict:
        return await self._dm.create_order(**kwargs)

    @with_schemas(output_schema=OrderOutputSchema)
    async def retrieve_order(self, order_id: int) -> dict:
        return await self._dm.retrieve_order(order_id=order_id)

    @with_schemas(OrdersFilterSchema, OrdersListSchema)
    async def list_orders(self, **kwargs) -> Dict[str, List[dict]]:
        got = await self._dm.list_orders(**kwargs)
        return {"orders": got}

    @with_schemas(AccountManagerIdFilterSchema, OrdersFilterSchema)
    async def retrieve_order_ids_for_account(self, **kwargs) -> Dict[str, List[int]]:
        got = await self._dm.retrieve_order_ids_for_account(**kwargs)
        return {"ids": got}
