# -*- coding: utf-8 -*-
"""
collective.iconifiedcategory
----------------------------

Created by mpeeters
:license: GPL, see LICENCE.txt for more details.
"""

from collective.documentviewer.async import queueJob
from collective.iconifiedcategory import _
from collective.iconifiedcategory import utils
from collective.iconifiedcategory.content.category import ICategory
from collective.iconifiedcategory.content.subcategory import ISubcategory
from collective.iconifiedcategory.event import IconifiedAttrChangedEvent
from collective.iconifiedcategory.interfaces import IIconifiedPrintable
from imio.helpers.cache import invalidate_cachekey_volatile_for
from plone import api
from plone.rfc822.interfaces import IPrimaryFieldInfo
from Products.statusmessages.interfaces import IStatusMessage
from zExceptions import Redirect
from zope.component import getAdapter
from zope.event import notify
from zope.lifecycleevent import IObjectAddedEvent
from zope.lifecycleevent import IObjectRemovedEvent


def categorized_content_created(obj, event):

    # if 'to_print' and 'confidential' are managed manually,
    # we may defer events if relevant value found in the REQUEST
    if obj.REQUEST.get('defer_categorized_content_created_event', False):
        return
    # set default values for to_print, confidential and to_sign/signed
    try:
        category = utils.get_category_object(obj, obj.content_category)
    except KeyError:
        return
    # left False if to_print/confidential/to_sign
    # not enabled on ContentCategoryGroup
    category_group = category.get_category_group(category)

    # only set default value if obj was not created with a to_print=True
    if category_group.to_be_printed_activated and not getattr(obj, 'to_print', False):
        obj.to_print = category.to_print
        # notifying IconifiedAttrChangedEvent for 'to_print' is done in categorized_content_updated
    elif not category_group.to_be_printed_activated:
        obj.to_print = False

    # only set default value if obj was not created with a confidential=True
    if category_group.confidentiality_activated and not getattr(obj, 'confidential', False):
        obj.confidential = category.confidential
        notify(IconifiedAttrChangedEvent(
            obj,
            'confidential',
            old_values={},
            new_values={'confidential': obj.confidential},
            is_created=True
        ))
    elif not category_group.confidentiality_activated:
        obj.confidential = False

    # only set default value if obj was not created with a to_sign=True or signed=True
    if category_group.signed_activated and not (getattr(obj, 'to_sign', False) or getattr(obj, 'signed', False)):
        obj.to_sign = category.to_sign
        obj.signed = category.signed
        notify(IconifiedAttrChangedEvent(
            obj,
            'to_sign',
            old_values={},
            new_values={'to_sign': obj.to_sign,
                        'signed': obj.signed},
            is_created=True
        ))

    elif not category_group.signed_activated:
        obj.to_sign = False
        obj.signed = False

    # only set default value if obj was not created with a publishable=True
    if category_group.publishable_activated and not getattr(obj, 'publishable', False):
        obj.publishable = category.publishable
        notify(IconifiedAttrChangedEvent(
            obj,
            'publishable',
            old_values={},
            new_values={'publishable': obj.publishable},
            is_created=True
        ))
    elif not category_group.publishable_activated:
        obj.publishable = False

    # to_print changed event is managed in categorized_content_updated
    categorized_content_updated(obj, event, is_created=True)

    if utils.is_file_type(obj.portal_type):
        file_field_name = IPrimaryFieldInfo(obj).fieldname
        size = getattr(obj, file_field_name).size
        if utils.warn_filesize(size):
            plone_utils = api.portal.get_tool('plone_utils')
            plone_utils.addPortalMessage(
                _("The annex that you just added has a large size and "
                  "could be difficult to download by users wanting to "
                  "view it!"), type='warning')


def content_updated(obj, event):
    categorized_content_updated(obj, event)


def categorized_content_updated(obj, event, is_created=False):
    category = utils.get_category_object(obj, obj.content_category)

    if category.show_preview in (1, 2):
        queueJob(obj)

    if hasattr(obj, 'to_print'):
        # if current 'to_print' is None, it means that current content
        # could not be printable, but as it changed,
        # in this case we use the default value
        if obj.to_print is None:
            category_group = category.get_category_group(category)
            if category_group.to_be_printed_activated:
                obj.to_print = category.to_print

        adapter = getAdapter(obj, IIconifiedPrintable)
        adapter.update_object()
        notify(IconifiedAttrChangedEvent(
            obj,
            'to_print',
            old_values={'to_print': obj.to_print},
            new_values={'to_print': obj.to_print},
            is_created=is_created
        ))
    # we may defer call to utils.update_categorized_elements
    # if relevant value found in the REQUEST
    # this is useful when adding several categorized elements without
    # calling update_categorized_elements between every added element
    if obj.REQUEST.get('defer_update_categorized_elements', False):
        return

    utils.update_categorized_elements(obj.aq_parent, obj, category)


def content_category_updated(event):
    if hasattr(event.object, 'content_category'):
        obj = event.object
        target = utils.get_category_object(obj, obj.content_category)
        utils.update_categorized_elements(
            obj.aq_parent,
            obj,
            target,
            limited=False,
            sort=event.sort,
            logging=True
        )


def categorized_content_moved(obj, event):
    if IObjectAddedEvent.providedBy(event):  # copy/paste or creation => IObjectAddedEvent
        return
    if IObjectRemovedEvent.providedBy(event):  # delete but not cut/paste
        utils.remove_categorized_element(obj.aq_parent, obj)
        return
    if obj.REQUEST.get('defer_update_categorized_elements', False):
        return
    category = utils.get_category_object(obj, obj.content_category)
    if event.oldName is not None and event.oldName != event.newName:  # rename
        utils.update_categorized_elements(obj.aq_parent, obj, category)
    elif event.oldParent is not None and event.newParent is not None and event.oldParent != event.newParent:  # move
        utils.update_categorized_elements(obj.aq_parent, obj, category)  # paste
        utils.remove_categorized_element(event.oldParent, obj)


def categorized_content_container_moved(container, event):
    """Update all categorized_elements when a parent object is renamed or pasted"""
    if IObjectRemovedEvent.providedBy(event):
        return
    if container.REQUEST.get('defer_update_categorized_elements', False) or \
            container.REQUEST.get('defer_categorized_content_created_event', False):
        return
    pc = api.portal.get_tool('portal_catalog')
    brains = pc.unrestrictedSearchResults(
        path={'query': '/'.join(container.getPhysicalPath())},
        object_provides='collective.iconifiedcategory.behaviors.iconifiedcategorization.IIconifiedCategorizationMarker'
    )
    parents = {b._unrestrictedGetObject().aq_parent for b in brains}  # use set to avoid parent duplicates
    for parent in parents:
        utils.update_all_categorized_elements(parent)


def category_before_remove(obj, event):
    # do not fail if removing the Plone Site
    if not event.object.meta_type == 'Plone Site' and \
       ICategory.providedBy(obj) is True:
        if utils.has_relations(obj) is True:
            IStatusMessage(obj.REQUEST).addStatusMessage(
                _('This category or one of is subcategory are used by '
                  'another object and cannot be deleted'),
                type='error',
            )
            raise Redirect(obj.REQUEST.get('HTTP_REFERER'))
        _cookCssResources()


def subcategory_before_remove(obj, event):
    # do not fail if removing the Plone Site
    if not event.object.meta_type == 'Plone Site' and \
       ISubcategory.providedBy(obj) is True:
        if utils.has_relations(obj) is True:
            IStatusMessage(obj.REQUEST).addStatusMessage(
                _('This subcategory is used by another object and cannot be '
                  'deleted'),
                type='error',
            )
            raise Redirect(obj.REQUEST.get('HTTP_REFERER'))


def category_moved(obj, event):
    if event.oldParent is None or event.newParent is None:
        return
    if utils.has_relations(obj) is True:
        IStatusMessage(obj.REQUEST).addStatusMessage(
            _('This category or one of is subcategory are used by '
              'another object and cannot be deleted'),
            type='error',
        )
        raise Redirect(obj.REQUEST.get('HTTP_REFERER'))
    _cookCssResources()


def subcategory_moved(obj, event):
    if event.oldParent is None or event.newParent is None:
        return
    if utils.has_relations(obj) is True:
        IStatusMessage(obj.REQUEST).addStatusMessage(
            _('This subcategory is used by another object and cannot be '
              'deleted'),
            type='error',
        )
        raise Redirect(obj.REQUEST.get('HTTP_REFERER'))


def _cookCssResources():
    # recook portal_css because we need
    # iconified-category.css to be compiled again as it is cached
    portal_css = api.portal.get_tool('portal_css')
    portal_css.cookResources()


def category_created(category, event):
    # make sure the 'listing' scale image is created
    category.restrictedTraverse('@@images').scale(scale='listing')
    _cookCssResources()


def container_modified(obj, event):
    """When a category container (so a CategoryGroup or a Category)
       is modified (meaning element added/removed/position changed)
       invalidate date used for utils.get_ordered_categories caching."""
    invalidate_cachekey_volatile_for(
        'collective.iconifiedcategory.utils.get_ordered_categories', get_again=True)
