from datetime import datetime
from typing import List, Optional

from maps_adv.billing_proxy.lib.api.api_providers.schemas import (
    ClientBindingsSchema,
    CpmCalculationInputSchema,
    CpmCalculationResultSchema,
    ProductAdviseInputSchema,
    ProductInfoSchema,
    ProductsInfoSchema,
    ServiceIdsSchema,
    CreateProductInputSchema,
    ProductIdSchema,
    UpdateProductInputSchema,
)
from maps_adv.billing_proxy.lib.domain import (
    CampaignType,
    CreativeType,
    CurrencyType,
    OrderSize,
    PlatformType,
    ProductsDomain,
    RubricName,
)

from .base import with_schemas


class ProductsApiProvider:
    _domain: ProductsDomain

    def __init__(self, domain: ProductsDomain):
        self._domain = domain

    @with_schemas(output_schema=ProductInfoSchema)
    async def retrieve_product(self, product_id: int):
        return await self._domain.retrieve_product(product_id)

    @with_schemas(output_schema=ProductsInfoSchema)
    async def list_products(self):
        return await self._domain.list_products()

    @with_schemas(ServiceIdsSchema, ProductsInfoSchema)
    async def list_products_by_service_ids(self, service_ids: Optional[int] = None):
        return await self._domain.list_products(service_ids=service_ids)

    @with_schemas(
        input_schema=ProductAdviseInputSchema, output_schema=ProductInfoSchema
    )
    async def advise_product(
        self,
        platform: PlatformType,
        campaign_type: CampaignType,
        client_id: Optional[int] = None,
        contract_id: Optional[int] = None,
        currency: Optional[CurrencyType] = None,
        service_id: Optional[int] = None,
        additional_platforms: List[PlatformType] = [],
    ):
        return await self._domain.advise_product(
            service_id=service_id,
            platforms=[platform] + additional_platforms,
            campaign_type=campaign_type,
            client_id=client_id,
            contract_id=contract_id,
            currency=currency,
        )

    @with_schemas(
        input_schema=CpmCalculationInputSchema, output_schema=CpmCalculationResultSchema
    )
    async def calculate_cpm(
        self,
        product_id: int,
        targeting_query: Optional[dict] = None,
        rubric: Optional[RubricName] = None,
        order_size: Optional[OrderSize] = None,
        creative_types: List[CreativeType] = None,
        dt: Optional[datetime] = None,
        active_from: Optional[datetime] = None,
        active_to: Optional[datetime] = None,
    ):
        return await self._domain.calculate_cpm(
            product_id=product_id,
            targeting_query=targeting_query,
            rubric_name=rubric,
            order_size=order_size,
            creative_types=creative_types,
            dt=dt,
            active_from=active_from,
            active_to=active_to,
        )

    @with_schemas(output_schema=ClientBindingsSchema)
    async def list_clients_bound_to_product(
        self,
        product_id,
    ):
        return await self._domain.list_clients_bound_to_product(product_id=product_id)

    @with_schemas(input_schema=ClientBindingsSchema)
    async def bind_client_to_product(
        self,
        product_id,
        clients,
    ):
        return await self._domain.bind_client_to_product(
            product_id=product_id, clients=clients
        )

    @with_schemas(input_schema=ClientBindingsSchema)
    async def unbind_client_from_product(
        self,
        product_id,
        clients,
    ):
        return await self._domain.unbind_client_from_product(
            product_id=product_id, clients=clients
        )

    @with_schemas(input_schema=CreateProductInputSchema, output_schema=ProductIdSchema)
    async def create_product(self, **kw_args):
        return await self._domain.create_product(**kw_args)

    @with_schemas(input_schema=UpdateProductInputSchema)
    async def update_product(self, **kw_args):
        await self._domain.update_product(**kw_args)
