import csv
from datetime import datetime
from io import StringIO
from operator import itemgetter

from maps_adv.billing_proxy.lib.api.api_providers.schemas import OrderNotify2Schema
from maps_adv.billing_proxy.lib.domain import OrdersDomain

from .exceptions import BadDtParameter, ExternalOrdersDoNotExist, OrdersDoNotExist


class BalanceApiProvider:
    _domain: OrdersDomain

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

    async def notify_orders(self, data) -> bool:
        # https://wiki.yandex-team.ru/Balance/Interfaces/Notifications/#balanceclient.notifyorder2
        # TODO: add logging
        order_updates_list = OrderNotify2Schema(many=True).load(data).data
        order_updates = {
            order_update["order_id"]: order_update
            for order_update in order_updates_list
        }
        service_ids = set(map(itemgetter("service_id"), order_updates_list))

        try:
            await self._domain.update_orders_limits(order_updates, service_ids)
        except OrdersDoNotExist:
            return False
        else:
            return True

    async def notify_orders_from_geoprod(self, data) -> bool:
        # https://wiki.yandex-team.ru/Balance/Interfaces/Notifications/#balanceclient.notifyorder2
        # TODO: add logging
        order_updates_list = OrderNotify2Schema(many=True).load(data).data
        order_updates = {
            order_update["order_id"]: order_update
            for order_update in order_updates_list
        }
        service_ids = set(map(itemgetter("service_id"), order_updates_list))

        try:
            await self._domain.update_orders_limits_from_geoprod(
                order_updates, service_ids
            )
        except ExternalOrdersDoNotExist:
            return False
        else:
            return True

    async def build_reconciliation_report(self, dt: str) -> str:
        try:
            due_to = datetime.strptime(dt, "%Y%m%d").date()
        except (TypeError, ValueError):
            raise BadDtParameter

        data = await self._domain.build_reconciliation_report(due_to=due_to)
        if not data:
            return ""

        csv_data = StringIO()
        writer = csv.DictWriter(
            csv_data, fieldnames=["order_id", "completion_qty", "consumption_qty"]
        )
        writer.writeheader()
        writer.writerows(
            {
                "order_id": order_id,
                "completion_qty": str(order_data["completion_qty"]),
                "consumption_qty": str(order_data["consumption_qty"]),
            }
            for order_id, order_data in data.items()
        )

        return csv_data.getvalue()

    async def load_geoprod_reconciliation_report(self, dt: str) -> str:
        try:
            for_date = datetime.strptime(dt, "%Y%m%d").date()
        except (TypeError, ValueError):
            raise BadDtParameter

        await self._domain.load_geoprod_reconciliation_report(for_date=for_date)
