from django.db import models
from django.utils.translation import gettext

from oscar.core.loading import get_class, get_model

Repository = get_class("shipping.repository", "Repository")

PaymentMethod = get_model("checkout", "PaymentMethod")


def payment_method_choices(user, basket, **filters):
    total = basket.total_incl_tax

    return [
        (method.key, gettext(method.label))
        for method in PaymentMethod.objects.filter(
            models.Q(max_amount__isnull=True) | models.Q(max_amount__gte=total),
            min_amount__lte=total,
            **filters,
        )
        if method.has_permission(user)
    ]


def get_available_shipping_methods(request, shipping_address=None):
    """
    Returns all applicable shipping method objects for a given basket.
    """
    # Shipping methods can depend on the user, the contents of the basket
    # and the shipping address (so we pass all these things to the
    # repository).  I haven't come across a scenario that doesn't fit this
    # system.
    return Repository().get_shipping_methods(
        basket=request.basket,
        user=request.user,
        shipping_addr=shipping_address,
        request=request,
    )


def get_default_shipping_method(request, shipping_address=None):
    return Repository().get_default_shipping_method(
        basket=request.basket,
        user=request.user,
        shipping_addr=shipping_address,
        request=request,
    )


def clean_shipping_method(request, basket, shipping_method_code, shipping_address):
    repo = Repository()

    default = repo.get_default_shipping_method(
        basket=basket,
        user=request.user,
        request=request,
        shipping_addr=shipping_address,
    )

    if shipping_method_code is not None:
        methods = repo.get_shipping_methods(
            basket=basket,
            user=request.user,
            request=request,
            shipping_addr=shipping_address,
        )

        find_method = (s for s in methods if s.code == shipping_method_code)
        shipping_method = next(find_method, default)
        return shipping_method

    return default
