# pylint: disable=W0622
import collections
import logging

from django.template.loader import render_to_string
from django.utils.translation import gettext as _
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.renderers import StaticHTMLRenderer
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.settings import api_settings
from rest_framework.views import APIView

from oscar.apps.address.models import Country
from oscar.core.loading import get_class, get_model

from oscarapi.views.basket import ShippingMethodView as BaseShippingMethodView

from ocyan.main.loading import get_ocyan_class

from ocyan.plugin.oscar_checkout.checkout.forms import (
    PaymentMethodForm,
    ShippingMethodForm,
)
from ocyan.plugin.oscar_checkout.checkout.utils import get_default_shipping_method
from ocyan.plugin.oscar_checkout.checkout.views import IndexView
from ocyan.plugin.oscar_checkout.utils import country_from_url

Repository = get_class("shipping.repository", "Repository")
ShippingAddress = get_model("order", "ShippingAddress")

payment_method_choices = get_class("checkout.utils", "payment_method_choices")

from .serializers import (
    OrderTotalSerializer,
    PaymentMethodSerializer,
    ShippingCountrySerializer,
    ShippingMethodSerializer,
)

CheckoutSerializer = get_ocyan_class("checkoutapi.serializers", "CheckoutSerializer")

logger = logging.getLogger(__name__)


@api_view(("GET",))
def api_root(request, format=None):
    apis = [
        (
            "login",
            reverse(
                "api-login", request=request, format=format, current_app="checkoutapi"
            ),
        ),
        (
            "basket-shipping-methods",
            reverse(
                "api-basket-shipping-methods",
                request=request,
                format=format,
                current_app="checkoutapi",
            ),
        ),
        (
            "basket-total",
            reverse(
                "api-basket-total",
                request=request,
                format=format,
                current_app="checkoutapi",
            ),
        ),
        (
            "countries",
            reverse(
                "country-list",
                request=request,
                format=format,
                current_app="checkoutapi",
            ),
        ),
    ]
    return Response(collections.OrderedDict(apis))


class ShippingMethodView(BaseShippingMethodView):
    serializer_class = ShippingCountrySerializer
    shipping_method_serializer_class = ShippingMethodSerializer
    renderer_classes = [StaticHTMLRenderer] + api_settings.DEFAULT_RENDERER_CLASSES

    def render_snippet(self, request, shiping_methods, basket, shipping_address=None):
        default_shipping_method = get_default_shipping_method(request, shipping_address)
        context = {
            "nolabel": True,
            "form": ShippingMethodForm(
                prefix="shipping_method_form",
                methods=shiping_methods,
                default=(
                    default_shipping_method.code
                    if default_shipping_method is not None
                    else None
                ),
            ),
            "title": _("Shipping method"),
            "request": request,
            "id": "shipping_method_form",
            "basket": basket,
        }

        return render_to_string("checkout/partials/shipping_method_block.html", context)

    def _get(self, request, shipping_address=None, format=None):  # pylint: disable=redefined-builtin
        basket = request.basket

        if shipping_address is not None:
            request.META["shipping_country"] = shipping_address.country.pk

        shiping_methods = Repository().get_shipping_methods(
            basket=basket,
            user=request.user,
            shipping_addr=shipping_address,
            request=request,
        )
        if format == "html":
            return Response(
                self.render_snippet(
                    request, shiping_methods, basket, shipping_address=shipping_address
                )
            )

        ser = self.shipping_method_serializer_class(
            shiping_methods, many=True, context={"basket": basket}
        )
        return Response(ser.data)

    def post(self, request, *args, format=None, **kwargs):  # pylint: disable=redefined-builtin
        s_ser = self.serializer_class(
            data=request.data, context={"request": request}, partial=True
        )
        shipping_address = None
        if s_ser.is_valid():
            shipping_address = ShippingAddress(**s_ser.validated_data)

        return self._get(request, format=format, shipping_address=shipping_address)


class OrderTotalView(APIView):
    serializer_class = CheckoutSerializer

    def get(self, request, format=None):
        return Response(
            {
                "shipping_method_code": "no-shipping-required",
                "shipping_address": {
                    "country": "http://127.0.0.1:9000/api/countries/nl/",
                    "first_name": "Henk",
                    "last_name": "Van den Heuvel",
                    "line1": "Roemerlaan 44",
                    "line2": "",
                    "line3": "",
                    "line4": "Kroekingen",
                    "notes": "Niet STUK MAKEN OK!!!!",
                    "phone_number": "+31 26 370 4887",
                    "postcode": "7777KK",
                    "state": "Gerendrecht",
                    "title": "Mr",
                },
            }
        )

    def post(self, request, format=None):
        data = request.data

        # this code is only needed for vat plugin
        try:
            shipping_country_url = data["shipping_address"]["country"]
            shipping_country = country_from_url(shipping_country_url)
            request.META["shipping_country"] = shipping_country
        except KeyError:
            pass

        basket = request.basket

        if basket.is_empty:
            return Response(status=status.HTTP_406_NOT_ACCEPTABLE)

        try:
            data["basket"] = reverse(
                "basket-detail",
                args=(basket.pk,),
                request=request,
                format=format,
                current_app="checkoutapi",
            )

        except TypeError:  # if data is a string; TypeError occurs
            return Response(status=status.HTTP_406_NOT_ACCEPTABLE)

        ser = self.serializer_class(
            data=data, context={"request": request}, partial=True
        )
        if ser.is_valid():
            checkout_data = ser.validated_data
            context = {
                "nohide": True,
                "id": "checkouttotal",
                "title": _("Your order"),
                "request": request,
                "user": request.user,
                "order_total": checkout_data["total"],
            }
            context.update(checkout_data)
            snippet = render_to_string("checkout/partials/totals_block.html", context)

            result_data = {"snippet": snippet}
            result_data.update(context)
            return Response(OrderTotalSerializer(result_data).data)

        return Response(ser.errors, status.HTTP_406_NOT_ACCEPTABLE)


class PaymentMethodView(APIView):
    serializer_class = PaymentMethodSerializer

    def get(self, request, format=None):
        return Response(
            IndexView(
                request=self.request, args=self.args, kwargs=self.kwargs
            ).get_payment_methods()
        )

    def post(self, request, format=None):
        data = request.data
        ser = self.serializer_class(data=data, context={"request": request})
        if ser.is_valid():
            val_data = ser.validated_data
            try:
                country = Country.objects.get(iso_3166_1_a2=val_data.get("country"))
            except Country.DoesNotExist:
                country = Country.objects.filter(is_shipping_country=True).first()

            results = payment_method_choices(
                request.user, request.basket, country=country
            )

            context = {
                "nolabel": True,
                "form": PaymentMethodForm(
                    prefix="payment_method_form", payment_method_choices=results
                ),
                "title": _("Payment method"),
                "request": request,
                "id": "payment_method_form",
            }

            snippet = render_to_string(
                "checkout/partials/payment_method_block.html", context
            )

            result_data = {"snippet": snippet}
            result_data.update(context)

            return Response(PaymentMethodSerializer(result_data).data)

        return Response(ser.errors, status.HTTP_406_NOT_ACCEPTABLE)
