from BTrees.OOBTree import OOBTree
from DateTime import DateTime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from json import loads
from logging import getLogger
from plone import api
from plone.memoize.view import memoize
from plone.protect.interfaces import IDisableCSRFProtection
from plone.registry.interfaces import IRegistry
from ploneintranet.admintool.browser.interfaces import IGeneratePWResetToken
from ploneintranet.admintool.interfaces import IStripeSettings
from ploneintranet.core import _
from Products.Five import BrowserView
from zope.annotation.interfaces import IAnnotations
from zope.component import getUtility
from zope.event import notify
from zope.i18n import translate
from zope.interface import alsoProvides
from zope.lifecycleevent import ObjectModifiedEvent

import stripe

logger = getLogger(__name__)


class StripeWebhooks(BrowserView):

    _annotations_key = "stripe"
    _workflow = "userprofile_workflow"

    @property
    @memoize
    def stripe_storage(self):
        """Get or create the stripe storage where we can save payment data"""
        annotations = IAnnotations(self.context)
        key = self._annotations_key
        if key not in annotations:
            annotations[key] = OOBTree()
        return annotations[key]

    @property
    @memoize
    def stripe_settings(self):
        registry = getUtility(IRegistry)
        return registry.forInterface(IStripeSettings)

    def get_secret_key(self, mode):
        settings = self.stripe_settings
        if mode == "live":
            return settings.live_secret_key
        else:
            return settings.test_secret_key

    def validate_data(self):
        event_json = loads(self.request.get("BODY"))
        mode = "live"
        if not event_json["livemode"]:
            mode = "test"
        stripe.api_key = self.get_secret_key(mode)

        data = stripe.Event.retrieve(event_json["id"])

        # Verify that the id, type, and created date of the event match
        if data["id"] != event_json["id"]:
            raise ValueError("Event id failed verification")
        if data["type"] != event_json["type"]:
            raise ValueError("Event type failed verification")
        if data["created"] != event_json["created"]:
            raise ValueError("Event creation date failed verification")
        return data

    @property
    @memoize
    def stripe_data(self):
        return self.validate_data()

    @property
    @memoize
    def user(self):
        data = self.stripe_data
        pc = api.portal.get_tool("portal_catalog")
        UID = data.data.object.client_reference_id
        for brain in pc.unrestrictedSearchResults(UID=UID):
            return brain._unrestrictedGetObject()

    def send_email(self):
        """Send an email to instruct the user on how to login"""
        user = self.user
        message = MIMEMultipart()
        alsoProvides(self.request, IGeneratePWResetToken)
        mailview = api.content.get_view("mail-subscription-paid", user, self.request)
        body = mailview()
        message.attach(MIMEText(body.encode("utf8"), "html", _charset="utf8"))
        portal = api.portal.get()
        proto = api.content.get_view("proto", portal, self.request)
        try:
            api.portal.send_email(
                recipient=user.email,
                subject=translate(
                    _(
                        "Your subscription to ${portal_title} is now active!",
                        mapping={"portal_title": proto.site_title},
                    ),
                    target_language=api.portal.get_current_language(),
                ),
                body=message,
                immediate=False,
            )
        except Exception as e:
            api.portal.show_message(str(e), self.request, "error")
            logger.exception("Error sending the email")

    def manage_payment(self):
        data = self.stripe_data
        user = self.user
        new_status = {
            "action": "stripe",
            "actor": user.getId(),
            "comments": "Stripe payment: %r" % data.id,
            "review_state": "enabled",
            "time": DateTime(),
        }
        pw = api.portal.get_tool("portal_workflow")
        pw.setStatusOf(self._workflow, user, new_status)
        [wf.updateRoleMappingsFor(user) for wf in pw.getWorkflowsFor(user)]
        intranet = api.portal.get().groups.get("intranet")
        if intranet:
            intranet.members.add(user.getId())
            notify(ObjectModifiedEvent(intranet))
        user.reindexObject()
        self.send_email()

    def __call__(self):
        data = self.validate_data()
        self.stripe_storage[data.id] = data
        self.manage_payment()
        alsoProvides(self.request, IDisableCSRFProtection)
        return "OK"
