# -*- coding: utf-8; -*-
#
# This file is part of Superdesk.
#
# Copyright 2013, 2014 Sourcefabric z.u. and contributors.
#
# For the full copyright and license information, please see the
# AUTHORS and LICENSE files distributed with this source code, or
# at https://www.sourcefabric.org/superdesk/license

import os
import logging
from flask import current_app as app
from superdesk.utc import utcnow, get_date
from eve.utils import date_to_str
from datetime import timedelta

logger = logging.getLogger(__name__)


def get_lock_id(*args):
    """Get id for task using all given args."""
    return '-'.join((str(x) for x in args))


def get_host_id(task):
    """Get host id for given task.

    It should be unique on process level.

    :param task: celery task
    """
    return '%s:%s' % (task.request.hostname, os.getpid())


def __get_running_key(name, id):
    return 'task-running-{}:{}'.format(name, id)


def is_task_running(name, id, update_schedule):
    """Test if the instance was never run before or is not currently running.

    If the instance is not already running, we set it as running using locking.
    """
    def set_if_not_running(pipe):
        last_updated = pipe.get(key)
        if last_updated:
            last_updated = get_date(str(last_updated))
            delta = last_updated + update_schedule
            if delta < now:
                logger.warn('Overwriting running key for {}:{}'.format(name, id))
                pipe.set(key, date_to_str(now))
                return True
            else:
                logger.warn('Task {}:{} is already running. last_updated={}'.format(name, id, last_updated))
                return False
        else:
            pipe.set(key, date_to_str(now))
            return True

    key = __get_running_key(name, id)
    now = utcnow()

    if 'minutes' in update_schedule:
        update_schedule = timedelta(minutes=update_schedule.get('minutes', 5))
    elif 'seconds' in update_schedule:
        update_schedule = timedelta(seconds=update_schedule.get('seconds', 10))

    is_set = __redis_transaction(set_if_not_running, key)
    return not is_set


def mark_task_as_not_running(name, id):
    def remove_key(pipe):
        is_removed = pipe.delete(key)
        return True if is_removed > 0 else False

    key = __get_running_key(name, id)
    removed = __redis_transaction(remove_key, key)
    if not removed:
        logger.error('Failed to set {}:{} as not running'.
                     format(name, id))
    return removed


def __redis_transaction(func, key):
    """Modified version of the transaction class from the Redis library.

    We want to exit if someone else is modifying the value.
    Convenience method for executing the callable `func` as a transaction
    while watching all keys specified in `watches`. The 'func' callable
    should expect a single argument which is a Pipeline object.
    """
    with app.redis.pipeline(True, None) as pipe:
        try:
            if key:
                pipe.watch(key)
            func_value = func(pipe)
            pipe.execute()
            return func_value
        except Exception as ex:
            print(ex)
            return False
