from aiohttp.web import Request, Response

from maps_adv.billing_proxy.lib.api.api_providers import ClientsApiProvider
from maps_adv.billing_proxy.lib.api.api_providers.exceptions import (
    ClientByUidDoesNotExist,
    ClientDoesNotExist,
    AgencyDoesNotExist,
)

from tvmauth import TvmClient
from tvmauth.exceptions import TicketParsingException


class Resources:
    __slots__ = (
        "_provider",
        "_tvm_client",
    )

    def __init__(self, provider: ClientsApiProvider, tvm_client: TvmClient):
        self._provider = provider
        self._tvm_client = tvm_client

    def _get_uid(self, request: Request):
        ticket = request.headers.get("X-Ya-User-Ticket")
        if ticket is None:
            return None
        try:
            return self._tvm_client.check_user_ticket(ticket).default_uid
        except TicketParsingException:
            return None

    async def retrieve_client(self, request: Request) -> Response:
        client_id = int(request.match_info["client_id"])
        try:
            result = await self._provider.retrieve_client(client_id=client_id)
        except ClientDoesNotExist:
            return Response(status=404)
        else:
            return Response(body=result)

    async def retrieve_clients_by_account_manager(self, request: Request) -> Response:
        body = await request.read()
        result = await self._provider.retrieve_clients_for_account(body)
        return Response(body=result)

    async def list_client_contracts(self, request: Request) -> Response:
        client_id = int(request.match_info["client_id"])
        result = await self._provider.list_client_contracts(client_id=client_id)
        return Response(body=result)

    async def list_agency_contracts(self, request: Request) -> Response:
        agency_id = int(request.match_info["agency_id"])
        result = await self._provider.list_agency_contracts(agency_id=agency_id)
        return Response(body=result)

    async def register_client(self, request: Request) -> Response:
        body = await request.read()
        result = await self._provider.register_client(body, uid=self._get_uid(request))
        return Response(body=result, status=201)

    async def create_client(self, request: Request) -> Response:
        body = await request.read()
        result = await self._provider.create_client(body)
        return Response(body=result, status=201)

    async def set_account_manager_for_client(self, request: Request) -> Response:
        body = await request.read()
        await self._provider.set_account_manager_for_client(body)
        return Response(status=200)

    async def find_client_by_uid(self, request: Request) -> Response:
        body = await request.read()
        try:
            result = await self._provider.find_client_by_uid(body)
        except ClientByUidDoesNotExist:
            return Response(status=404)

        return Response(body=result, status=200)

    async def list_agencies(self, request: Request) -> Response:
        result = await self._provider.list_agencies()
        return Response(body=result)

    async def list_agency_clients(self, request: Request) -> Response:
        agency_id = request.match_info["agency_id"]

        if agency_id == "internal":
            result = await self._provider.list_internal_clients()
        else:
            result = await self._provider.list_agency_clients(agency_id=agency_id)

        return Response(body=result)

    async def add_clients_to_agency(self, request: Request) -> Response:
        agency_id = request.match_info["agency_id"]
        body = await request.read()

        if agency_id == "internal":
            await self._provider.add_clients_to_internal(body)
        else:
            await self._provider.add_clients_to_agency(body, agency_id=agency_id)

        return Response(status=201)

    async def remove_clients_from_agency(self, request: Request) -> Response:
        agency_id = request.match_info["agency_id"]
        body = await request.read()

        if agency_id == "internal":
            await self._provider.remove_clients_from_internal(body)
        else:
            await self._provider.remove_clients_from_agency(body, agency_id=agency_id)

        return Response(status=204)

    async def set_client_has_accepted_offer(self, request: Request) -> Response:
        client_id = int(request.match_info["client_id"])
        try:
            await self._provider.set_client_has_accepted_offer(
                client_id=client_id, is_agency=False
            )
        except ClientDoesNotExist:
            return Response(status=404)
        else:
            return Response()

    async def set_agency_has_accepted_offer(self, request: Request) -> Response:
        agency_id = int(request.match_info["agency_id"])
        try:
            await self._provider.set_client_has_accepted_offer(
                client_id=agency_id, is_agency=True
            )
        except AgencyDoesNotExist:
            return Response(status=404)
        else:
            return Response()

    async def list_clients(self, request: Request) -> Response:
        result = await self._provider.list_clients()
        return Response(body=result)

    async def _add_user_to_client(self, request: Request, is_agency: bool) -> Response:
        client_id = int(request.match_info["agency_id" if is_agency else "client_id"])
        uid = int(request.match_info["uid"])
        try:
            await self._provider.add_user_to_client(
                client_id=client_id, is_agency=is_agency, uid=uid
            )
        except ClientDoesNotExist:
            return Response(status=404)
        except AgencyDoesNotExist:
            return Response(status=404)
        else:
            return Response()

    async def add_user_to_client(self, request: Request) -> Response:
        return await self._add_user_to_client(request, is_agency=False)

    async def add_user_to_agency(self, request: Request) -> Response:
        return await self._add_user_to_client(request, is_agency=True)

    async def _list_client_representatives(self, request: Request, is_agency: bool) -> Response:
        client_id = int(request.match_info["agency_id" if is_agency else "client_id"])
        try:
            result = await self._provider.list_client_representatives(
                client_id=client_id, is_agency=is_agency
            )
            return Response(body=result)
        except ClientDoesNotExist:
            return Response(status=404)
        except AgencyDoesNotExist:
            return Response(status=404)

    async def list_client_representatives(self, request: Request) -> Response:
        return await self._list_client_representatives(request, is_agency=False)

    async def list_agency_representatives(self, request: Request) -> Response:
        return await self._list_client_representatives(request, is_agency=True)
