from . import command, USER_SET, users_map, ensure_config
from telegram import ChatPermissions
import threading
import regex
from pytz import utc
from datetime import datetime, timedelta
import logging

config = None
logger = logging.getLogger('silence')

def get_seconds_for_timespan(timespan: str):
    if timespan in ('week', 'wk', 'w'):
        return 604800
    if timespan in ('day', 'd'):
        return 86400
    if timespan in ('hour', 'hr', 'h'):
        return 3600
    if timespan in ('minute', 'min', 'm'):
        return 60
    return 1

TIMESPANS = r"(?:(?P<number>\d+) ?(?P<span>week|wk|w|day|d|hour|hr|h|minute|min|m|second|sec|s)s?)"
SIMPLE_SPAN_RE = regex.compile(r"{}( ?\+? ?{})*".format(TIMESPANS,
                                                        TIMESPANS), regex.V1)


def get_simple_ts(msg):
    m = SIMPLE_SPAN_RE.search(msg)
    logger.debug(f"Found timestamp match: {m}")

    if not m: return None

    date_parts = [*zip(m.captures('number'), m.captures('span'))]
    length = 0
    for num, span in date_parts:
        length += get_seconds_for_timespan(span) * int(num)

    t = timedelta(seconds=length)
    logger.debug(f"timedelta: {t}")
    return (m.group(0), datetime.now(utc) + t)


def copy_cm_state(chat_member):
    state = {}
    for k, v in vars(chat_member).items():
        if not any(isinstance(v, t) for t in (str, int, bool)):
            continue
        state[k] = v
    return state


def set_cm_state(bot, uid, chat_id, state):
    perms = ChatPermissions()
    for var, _ in vars(perms).items():
        if var in state:
            setattr(perms, var, state[var])
        else:
            setattr(perms, var, True)

    bot.restrict_chat_member(chat_id, uid, perms)

    if state['status'] == 'administrator':
        admin_rights = {k:v for k, v in state.items() if k in ('can_change_info', 'can_delete_messages', 'can_invite_users', 'can_restrict_members', 'can_pin_messages', 'can_promote_members', 'is_anonymous')}
        bot.promote_chat_member(chat_id, uid, **admin_rights)

    if (title := state.get('custom_title')):
        bot.set_chat_administrator_custom_title(chat_id, uid, title)


def start_unsilence_bang_timer(bot, chat_id, uid, endts):
    def unsilence_bang_single(chat_id, uid):
        del config["silence_bang_timers"][(chat_id, uid)]
        if (cm_state := config['silence_stored_rights'].get((chat_id, uid))) is None:
            return

        set_cm_state(bot, uid, chat_id, cm_state)

    tmr = threading.Timer((endts - datetime.now(utc)).total_seconds(), unsilence_bang_single, (chat_id, uid))
    tmr.start()


@command(f'silence! ({USER_SET})(?: for (.+))?', auth=True, pass_groups=True)
def silence_bang(bea, bot, update, groups):
    """
    Silence a user, by limiting their rights.
    If they are currently an admin, try to remove their admin rights.
    """
    users = groups[0]
    time = groups[1]
    if time is not None:
        time = get_simple_ts(time)
        if time is None:
            return bea.reply("Couldn't parse that timestamp")
    chat_id = update.effective_chat.id

    def silence_bang_single(uid):
        chat_member = update.effective_chat.get_member(uid)
        if chat_member.status not in ('administrator', 'member', 'restricted'):
            return
        if chat_member.status == 'creator':
            return bea.reply("Cannot silence the creator of a group.")
        cm_state = copy_cm_state(chat_member)
        config['silence_stored_rights'][(chat_id, uid)] = cm_state
        bot.promote_chat_member(chat_id, uid,
                                can_change_info=False,
                                can_delete_messages=False,
                                can_invite_users=False,
                                can_restrict_members=False,
                                can_pin_messages=False,
                                can_promote_members=False)
        perms = ChatPermissions()
        for var, _ in vars(perms).items():
            setattr(perms, var, False)
        bot.restrict_chat_member(chat_id, uid, perms)
        if time is not None:
            config['silence_bang_timers'][(chat_id, uid)] = time[1]
            start_unsilence_bang_timer(bot, chat_id, uid, time[1])

    users_map(bea, users, silence_bang_single)


@command(f"unsilence! ({USER_SET})", auth=True, pass_groups=True)
def unsilence_bang(bea, bot, update, groups):
    """
    Unsilence a user silenced with silence!, restoring their previous rights.
    """
    user = groups[0]
    chat_id = update.effective_chat.id

    def unsilence_bang_single(uid):
        if (cm_state := config['silence_stored_rights'].get((chat_id, uid))) is None:
            return

        set_cm_state(bot, uid, chat_id, cm_state)

    users_map(bea, user, unsilence_bang_single)


@command('silence (' + USER_SET + ')', auth=True, pass_groups=True)
def silence(bea, bot, update, groups):
    """ Silence a user, deleting any messages they send. """
    user = groups[0]

    def silence_single(uid):
        f = lambda bea, bot, update, **kwargs: update.message.delete() if update.effective_user.id == uid else None
        f._silenced_user = uid
        bea.commands.insert(0, f)
    users_map(bea, user, silence_single)


@command(f'unsilence ({USER_SET})', auth=True, pass_groups=True)
def unsilence(bea, bot, update, groups):
    """ Unsilence a user, allowing them to speak again. """
    user = groups[0]

    def unsilence_single(uid):
        for command in bea.commands:
            if hasattr(command, '_silenced_user') and command._silenced_user == uid:
                break
        else:
            return
        bea.commands.remove(command)

    users_map(bea, user, unsilence_single)


@command('mute (' + USER_SET + ')', pass_groups=True, auth=True)
def mute(bea, bot, update, groups):
    """ Mute a user, making Bea unresponsive to commands from them. """
    uname = groups[0]
    users_map(bea, uname, lambda uid: bea.banned_uids.add(uid))


@command('unmute (' + USER_SET + ')', pass_groups=True, auth=True)
def unmute(bea, bot, update, groups):
    """ Unmute a user, allowing them to use Bea's commands again. """
    uname = groups[0]
    def unmute_single(uid):
        if uid in bea.banned_uids:
            bea.banned_uids.remove(uid)
    users_map(bea, uname, unmute_single)


def init(bea, _config):
    global config
    config = _config
    ensure_config(config, silence_bang_timers={}, mute_timers={}, silence_stored_rights={})
    return [silence_bang, unsilence_bang, silence, unsilence, mute, unmute]
