from urllib.parse import urlunparse

from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.contrib.sites.shortcuts import get_current_site
from django.template import Context, Template
from django.urls import NoReverseMatch, reverse

from ocyan.core.fender import config
from ocyan.main.loading import get_ocyan_classes

# pylint: disable=unbalanced-tuple-unpacking
(get_wagtailsettings, get_offline_request) = get_ocyan_classes(
    "wagtail_utils.utils", ["get_wagtailsettings", "get_offline_request"]
)


# pylint: disable=abstract-method
class AnonymousFullNameUser(AnonymousUser):
    def __init__(self, first_name, last_name, email):
        self.first_name = first_name
        self.last_name = last_name
        self.email = email

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = "%s %s" % (self.first_name or "", self.last_name or "")
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name


class EmailContext(dict):
    def __init__(self, site=None, request=None, **kwargs):
        if site is not None and request is None:
            request = get_offline_request(site.domain)
        elif site is None and request is not None:
            site = get_current_site(request)

        super().__init__(site=site, request=request, **kwargs)

        self.populate_props()

    def __getattr__(self, attname):
        return self.get(attname)

    def populate_props(self):
        self.setdefault("settings", self.settings)

    @property
    def site(self):
        return self["site"]

    @property
    def settings(self):
        return get_wagtailsettings(self.site.domain)


class OrderContext(EmailContext):
    def __init__(self, order, **kwargs):
        kwargs.setdefault("request", get_offline_request(order.site.domain))
        kwargs.setdefault("site", order.site)
        super().__init__(order=order, **kwargs)

    def populate_props(self):
        super().populate_props()
        self.setdefault("user", self.user)
        self.setdefault("lines", self.lines)
        self.setdefault("status_url", self.status_url)
        self.setdefault(
            "static_base_url", getattr(settings, "OSCAR_STATIC_BASE_URL", None)
        )
        self.setdefault("user", self.user)

    @property
    def user(self):
        _user = self.order.user
        _shipping_address = self.order.shipping_address

        # try to fill in user details for anonymous orders
        if self.order.is_anonymous and _shipping_address:
            return AnonymousFullNameUser(
                _shipping_address.first_name,
                _shipping_address.last_name,
                self.order.guest_email,
            )
        elif (
            _user
        ):  # this is not an anonymous order, but try to complete unfilled user details
            if _user.last_name or _user.first_name:
                return _user
            elif _shipping_address:
                _user.last_name = _shipping_address.last_name
                _user.first_name = _shipping_address.first_name
                return _user

            return _user
        elif _shipping_address:  # user has deleted profile.
            return AnonymousFullNameUser(
                _shipping_address.first_name,
                _shipping_address.last_name,
                self.order.guest_email,
            )

        # we know absolutely nothing about this person
        return AnonymousUser()

    @property
    def lines(self):
        return self.order.lines.all()

    @property
    def status_url(self):
        if not self.user.is_authenticated:
            # Attempt to add the anon order status URL to the email template
            # ctx.
            try:
                path = reverse(
                    "customer:anon-order",
                    kwargs={
                        "order_number": self.order.number,
                        "hash": self.order.verification_hash(),
                    },
                )
                return urlunparse(("https", self.site.domain, path, None, None, None))
            except NoReverseMatch:
                # We don't care that much if we can't resolve the URL
                return None

        return None


class ShippingContext(OrderContext):
    def __init__(self, shipping_event, **kwargs):
        self.shipping_event = shipping_event
        super().__init__(shipping_event.order, **kwargs)

    def populate_props(self):
        super().populate_props()
        self.setdefault("tracking_url", self.tracking_url)

        # hasattr because tracking is OneToOneField
        if hasattr(self.shipping_event, "tracking"):
            self.setdefault("tracking", self.shipping_event.tracking)

    @property
    def tracking_url(self):
        # hasattr because tracking is OneToOneField
        if hasattr(self.shipping_event, "tracking"):
            return self.shipping_event.tracking.get_tracking_url
        elif self.shipping_event.notes:
            maybe_an_url = self.shipping_event.notes
            tracking_url_template = config.get(
                "oscar_communications", "tracking_url_template", None
            )

            if maybe_an_url.startswith("http"):
                return maybe_an_url
            elif tracking_url_template is not None:
                tracking_template = Template(tracking_url_template)
                return tracking_template.render(Context({"code": maybe_an_url}))

        return None

    @property
    def lines(self):
        return [
            (lq.line, lq.quantity) for lq in self.shipping_event.line_quantities.all()
        ]
