import logging

from django.db.models import Q

from staff.celery_app import app
from staff.lib.db import atomic
from staff.lib.tasks import LockedTask

from staff.person.models import Staff
from staff.preprofile.models import Preprofile
from staff.person_avatar.models import AvatarMetadata


logger = logging.getLogger('person_avatar.tasks')


@app.task
class CheckUserPhotos(LockedTask):

    def get_lock_name(self, owner_id, owner_type='person', *args, **kwargs):
        return '{owner_type}-{owner_id}-updating-image-lock'.format(
            owner_type=owner_type,
            owner_id=owner_id,
        )

    def locked_run(self, owner_id, owner_type='person', *args, **kwargs):
        from .controllers import PersonAvatar, PreprofileAvatar

        owner_types = {
            'person': (Staff, PersonAvatar),
            'preprofile': (Preprofile, PreprofileAvatar),
        }
        owner_model, avatar_ctl = owner_types[owner_type]
        try:
            owner = owner_model.objects.select_related('extra').get(id=owner_id)
        except owner_model.DoesNotExist:
            logger.warning('Failed to retrieve owner '
                           'with id=%s from database' % owner_id)
            return

        for attr in ['main', 'avatar', 'gravatar']:
            attr_img = attr + '_img_for_upload'
            img = getattr(owner.extra, attr_img)
            if img is None:
                continue

            if owner_type == 'person':
                if attr == 'gravatar' and not getattr(owner, 'work_email'):
                    logger.warning(
                        'Failed to create gravatar '
                        'User {login} has no work_email'.format(login=owner.login)
                    )
                    setattr(owner.extra, attr_img, None)
                    continue

            with atomic():
                try:
                    avatar_ctl(owner, avatar_metadata=img).make(attr)
                    setattr(owner.extra, attr_img, None)
                    owner.extra.save()
                    logger.debug(
                        'Update extra field {attr} for {login}, set "{value}"'
                        .format(login=owner.login, attr=attr_img, value=None)
                    )
                except Exception:
                    logger.exception(
                        'Failed to set "{attr}" for user {login}'
                        .format(attr=attr_img, login=owner.login)
                    )
                    raise


@app.task(ignore_result=True)
def check_users_photos():
    q_main = Q(extra__main_img_for_upload=None)
    q_avatar = Q(extra__avatar_img_for_upload=None)
    q_gravatar = Q(extra__gravatar_img_for_upload=None)

    staff = Staff.objects.exclude(q_main & q_avatar & q_gravatar)
    for person in staff:
        CheckUserPhotos(owner_id=person.id, owner_type='person')


@app.task(ignore_result=True)
def check_preprofiles_photos():
    q_main = Q(extra__main_img_for_upload=None)
    q_avatar = Q(extra__avatar_img_for_upload=None)
    q_gravatar = Q(extra__gravatar_img_for_upload=None)

    staff = Staff.objects.exclude(q_main & q_avatar & q_gravatar)
    for person in staff:
        CheckUserPhotos(person_id=person.id)


@app.task(ignore_result=False)
def change_preprofile_login(preprofile_id, old_login):
    from staff.person_avatar.controllers import PreprofileAvatarCollection, MAIN, AVATAR
    preprofile = Preprofile.objects.get(id=preprofile_id)
    if preprofile.login == old_login:
        logger.info('Preprofile %s login has not been changed.', preprofile.id)

    collection = PreprofileAvatarCollection(owner=preprofile)
    if collection.count() == 0:
        logger.info('Preprofile %s have no avatars.', preprofile.id)
        return

    avatar = collection.first()
    collection.make(avatar.id, [MAIN, AVATAR], old_login=old_login)


@app.task(ignore_result=True)
def reinit_photos(avatar_attr, person_logins=None):  # переписать всё
    from .controllers import PersonAvatarCollection

    allowed_attrs = ['avatar', 'gravatar', 'main']
    if avatar_attr not in allowed_attrs:
        logger.info('Wrong avatar_attr parameter %s.'
                    'Correct parameters are: %s' % (avatar_attr, allowed_attrs))
        return

    attr = 'is_avatar' if avatar_attr == 'gravatar' else 'is_' + avatar_attr
    action = 'make_' + avatar_attr

    avatars_filter = {'is_deleted': False, attr: True}
    if person_logins:
        avatars_filter['person__login__in'] = person_logins

    avatar_ids = AvatarMetadata.objects.filter(**avatars_filter).values_list('id', flat=True)
    for avatar_metadata_id in avatar_ids:
        collection = PersonAvatarCollection(by_metadata_id=avatar_metadata_id)
        getattr(collection, action)(avatar_metadata_id, marks=[avatar_attr])


@app.task(bind=True)
def upload_preprofile_photo(self, preprofile_ids=None):
    from .controllers import PreprofileAvatarCollection
    preprofiles = Preprofile.objects.exclude(photo=None)
    if preprofile_ids:
        preprofiles = preprofiles.filter(id__in=preprofile_ids)

    for preprofile_instance in preprofiles:
        photo_url = preprofile_instance.photo
        if not photo_url:
            logger.info('Nothing to upload for preprofile %s', preprofile_instance.id)
            continue

        try:
            with atomic():
                preprofile_collection = PreprofileAvatarCollection(owner=preprofile_instance)
                preprofile_collection.upload(picture_url=photo_url)
                preprofile_instance.photo = ''
                preprofile_instance.save()
        except Exception as exc:
            logger.error(
                'Error trying to upload preprofile photo for %s', preprofile_instance.id
            )
            self.retry(countdown=60*5, exc=exc, max_retries=3)
