from datetime import datetime
from decimal import Decimal
from typing import Dict, List, Optional

from maps_adv.billing_proxy.lib.api.api_providers.schemas import (
    AccountManagerIdSchema,
    OrderCreationInputSchema,
    OrderDepositRequestCreationInputSchema,
    OrderDepositRequestSchema,
    OrderIdsSchema,
    OrdersChargeInputSchema,
    OrdersChargeOutputSchema,
    OrderSchema,
    OrdersSchema,
    OrdersStatInfo,
    OrdersStatInfoInput,
    OrderUpdateInputSchema,
    OrdersDiscountInfoInput,
    OrdersDiscountInfo,
    OrdersDebitsInfoSchema,
    OrdersDebitsInfoInputSchema,
)
from maps_adv.billing_proxy.lib.domain import OrdersDomain

from .base import with_schemas


class OrdersApiProvider:
    _domain: OrdersDomain

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

    @with_schemas(input_schema=OrderCreationInputSchema, output_schema=OrderSchema)
    async def create_order(
        self,
        title: str,
        client_id: int,
        product_id: int,
        contract_id: int,
        text: str,
        comment: str,
        service_id: int,
        agency_id: Optional[int] = None,
    ):
        return await self._domain.create_order(
            title=title,
            text=text,
            comment=comment,
            client_id=client_id,
            agency_id=agency_id,
            contract_id=contract_id,
            product_id=product_id,
            service_id=service_id,
        )

    @with_schemas(input_schema=OrderUpdateInputSchema, output_schema=OrderSchema)
    async def update_order(
        self, order_id: int, title: str, text: str, comment: str
    ) -> dict:
        return await self._domain.update_order(
            order_id=order_id, title=title, text=text, comment=comment
        )

    @with_schemas(output_schema=OrderSchema)
    async def retrieve_order(self, order_id: int):
        return await self._domain.retrieve_order(order_id)

    @with_schemas(output_schema=OrderSchema)
    async def retrieve_order_by_external_id(self, external_id: int):
        return await self._domain.retrieve_order_by_external_id(external_id)

    @with_schemas(input_schema=OrderIdsSchema, output_schema=OrdersSchema)
    async def retrieve_orders(self, order_ids: List[int]):
        return await self._domain.retrieve_orders(order_ids)

    @with_schemas(input_schema=AccountManagerIdSchema, output_schema=OrderIdsSchema)
    async def retrieve_order_ids_for_account(self, account_manager_id: int):
        return await self._domain.retrieve_order_ids_for_account(account_manager_id)

    @with_schemas(output_schema=OrdersSchema)
    async def list_agency_orders(self, agency_id: str, client_id: Optional[int] = None):
        return await self._domain.list_agency_orders(int(agency_id), client_id)

    @with_schemas(output_schema=OrdersSchema)
    async def list_client_orders(self, client_id: str):
        return await self._domain.list_client_orders(client_id)

    @with_schemas(output_schema=OrdersSchema)
    async def list_internal_orders(self, client_id: Optional[int] = None):
        return await self._domain.list_agency_orders(None, client_id)

    @with_schemas(
        input_schema=OrderDepositRequestCreationInputSchema,
        output_schema=OrderDepositRequestSchema,
    )
    async def create_deposit_request(
        self, order_id: int, amount: Decimal, region: Optional[str] = None
    ):
        return await self._domain.create_deposit_request(order_id, amount, region)

    @with_schemas(input_schema=OrdersStatInfoInput, output_schema=OrdersStatInfo)
    async def list_orders_stats(self, order_ids: List[int]) -> Dict[int, dict]:
        return await self._domain.list_orders_stats(order_ids)

    @with_schemas(
        input_schema=OrdersDiscountInfoInput, output_schema=OrdersDiscountInfo
    )
    async def list_orders_discounts(
        self, order_ids: List[int], billed_at: datetime
    ) -> Dict[int, dict]:
        return await self._domain.list_orders_discounts(order_ids, billed_at)

    @with_schemas(
        input_schema=OrdersChargeInputSchema, output_schema=OrdersChargeOutputSchema
    )
    async def charge_orders(
        self, orders_charge: List[dict], bill_for_timestamp: datetime
    ) -> Dict:
        charge_info = {
            order_charge["order_id"]: order_charge["charged_amount"]
            for order_charge in orders_charge
        }

        charge_result, applied = await self._domain.charge_orders(
            charge_info=charge_info, bill_for_dt=bill_for_timestamp
        )

        return {"charge_result": charge_result, "applied": applied}

    @with_schemas(input_schema=OrderIdsSchema, output_schema=OrderIdsSchema)
    async def list_active_orders(self, order_ids: List[int]) -> List[int]:
        order_ids = order_ids or None
        return await self._domain.list_active_orders(order_ids)

    @with_schemas(
        input_schema=OrdersDebitsInfoInputSchema, output_schema=OrdersDebitsInfoSchema
    )
    async def list_orders_debits(
        self, order_ids: List[int], billed_after: datetime
    ) -> Dict[int, dict]:
        return await self._domain.list_orders_debits(order_ids, billed_after)
