import re
import textwrap

from django.utils import timezone
from django.utils.translation import ugettext_lazy as _

from orchestra.apps.orchestration import ServiceController
from orchestra.apps.resources import ServiceMonitor

from . import settings
from .models import List


class MailmanBackend(ServiceController):
    verbose_name = "Mailman"
    model = 'lists.List'
    addresses = [
        '',
        '-admin',
        '-bounces',
        '-confirm',
        '-join',
        '-leave',
        '-owner',
        '-request',
        '-subscribe',
        '-unsubscribe'
    ]
    
    def include_virtual_alias_domain(self, context):
        # TODO  for list virtual_domains cleaning up we need to know the old domain name when a list changes its address
        #       domain, but this is not possible with the current design.
        #       sync the whole file everytime?
        # TODO same for mailbox virtual domains
        if context['address_domain']:
            self.append(textwrap.dedent("""
                [[ $(grep "^\s*%(address_domain)s\s*$" %(virtual_alias_domains)s) ]] || {
                    echo "%(address_domain)s" >> %(virtual_alias_domains)s
                    UPDATED_VIRTUAL_ALIAS_DOMAINS=1
                }""") % context
            )
    
    def exclude_virtual_alias_domain(self, context):
        address_domain = context['address_domain']
        if not List.objects.filter(address_domain=address_domain).exists():
            self.append('sed -i "/^%(address_domain)s\s*$/d" %(virtual_alias_domains)s' % context)
    
    def get_virtual_aliases(self, context):
        aliases = []
        for address in self.addresses:
            context['address'] = address
            aliases.append("%(address_name)s%(address)s@%(domain)s\t%(name)s%(address)s" % context)
        return '\n'.join(aliases)
    
    def save(self, mail_list):
        context = self.get_context(mail_list)
        # Create list
        self.append(textwrap.dedent("""\
            [[ ! -e %(mailman_root)s/lists/%(name)s ]] && {
                newlist --quiet --emailhost='%(domain)s' '%(name)s' '%(admin)s' '%(password)s'
            }""") % context)
        # Custom domain
        if mail_list.address:
            context['aliases'] = self.get_virtual_aliases(context)
            # Preserve indentation
            self.append(textwrap.dedent("""\
                if [[ ! $(grep '\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
                    echo '# %(banner)s\n%(aliases)s
                    ' >> %(virtual_alias)s
                    UPDATED_VIRTUAL_ALIAS=1
                else
                    if [[ ! $(grep '^\s*%(address_name)s@%(address_domain)s\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
                        sed -i -e '/^.*\s%(name)s\(%(address_regex)s\)\s*$/d' \\
                               -e 'N; /^\s*\\n\s*$/d; P; D' %(virtual_alias)s
                        echo '# %(banner)s\n%(aliases)s
                        ' >> %(virtual_alias)s
                        UPDATED_VIRTUAL_ALIAS=1
                    fi
                fi""") % context
            )
            self.append(
                'echo "require_explicit_destination = 0" | '
                '%(mailman_root)s/bin/config_list -i /dev/stdin %(name)s' % context
            )
            self.append(textwrap.dedent("""\
                echo "host_name = '%(address_domain)s'" | \
                    %(mailman_root)s/bin/config_list -i /dev/stdin %(name)s""") % context
            )
        else:
            # Cleanup shit
            self.append(textwrap.dedent("""\
                if [[ ! $(grep '\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
                    sed -i "/^.*\s%(name)s\s*$/d" %(virtual_alias)s
                fi""") % context
            )
        # Update
        if context['password'] is not None:
            self.append(
                '%(mailman_root)s/bin/change_pw --listname="%(name)s" --password="%(password)s"' % context
            )
        self.include_virtual_alias_domain(context)
    
    def delete(self, mail_list):
        context = self.get_context(mail_list)
        self.exclude_virtual_alias_domain(context)
        self.append(textwrap.dedent("""\
            sed -i -e '/^.*\s%(name)s\(%(address_regex)s\)\s*$/d' \\
                   -e 'N; /^\s*\\n\s*$/d; P; D' %(virtual_alias)s""") % context
        )
        self.append("rmlist -a %(name)s" % context)
    
    def commit(self):
        context = self.get_context_files()
        self.append(textwrap.dedent("""
            [[ $UPDATED_VIRTUAL_ALIAS == 1 ]] && { postmap %(virtual_alias)s; }
            [[ $UPDATED_VIRTUAL_ALIAS_DOMAINS == 1 ]] && { /etc/init.d/postfix reload; }
            """) % context
        )
    
    def get_context_files(self):
        return {
            'virtual_alias': settings.LISTS_VIRTUAL_ALIAS_PATH,
            'virtual_alias_domains': settings.LISTS_VIRTUAL_ALIAS_DOMAINS_PATH,
        }
    
    def get_banner(self, mail_list):
        banner = super(MailmanBackend, self).get_banner()
        return '%s %s' % (banner, mail_list.name)
    
    def get_context(self, mail_list):
        context = self.get_context_files()
        context.update({
            'banner': self.get_banner(mail_list),
            'name': mail_list.name,
            'password': mail_list.password,
            'domain': mail_list.address_domain or settings.LISTS_DEFAULT_DOMAIN,
            'address_name': mail_list.get_address_name(),
            'address_domain': mail_list.address_domain,
            'address_regex': '\|'.join(self.addresses),
            'admin': mail_list.admin_email,
            'mailman_root': settings.LISTS_MAILMAN_ROOT_PATH,
        })
        return context


class MailmanTraffic(ServiceMonitor):
    model = 'lists.List'
    resource = ServiceMonitor.TRAFFIC
    verbose_name = _("Mailman traffic")
    
    def prepare(self):
        super(MailmanTraffic, self).prepare()
        current_date = timezone.localtime(self.current_date)
        current_date = current_date.strftime("%b %d %H:%M:%S")
        self.append(textwrap.dedent("""\
            function monitor () {
                OBJECT_ID=$1
                LAST_DATE=$2
                LIST_NAME="$3"
                MAILMAN_LOG="$4"
                
                SUBSCRIBERS=$(list_members ${LIST_NAME} | wc -l)
                SIZE=$(grep ' post to ${LIST_NAME} ' "${MAILMAN_LOG}" \\
                       | awk '"$LAST_DATE"<=$0 && $0<="%s"' \\
                       | sed 's/.*size=\([0-9]*\).*/\\1/' \\
                       | tr '\\n' '+' \\
                       | xargs -i echo {} )
                echo ${OBJECT_ID} $(( ${SIZE}*${SUBSCRIBERS} ))
            }""") % current_date)
    
    def monitor(self, mail_list):
        context = self.get_context(mail_list)
        self.append(
            'monitor %(object_id)i %(last_date)s "%(list_name)s" "%(log_file)s{,.1}"' % context)
    
    def get_context(self, mail_list):
        last_date = timezone.localtime(self.get_last_date(mail_list.pk))
        return {
            'mailman_log': settings.LISTS_MAILMAN_POST_LOG_PATH,
            'list_name': mail_list.name,
            'object_id': mail_list.pk,
            'last_date': last_date.strftime("%b %d %H:%M:%S"),
        }


class MailmanSubscribers(ServiceMonitor):
    model = 'lists.List'
    verbose_name = _("Mailman subscribers")
    
    def monitor(self, mail_list):
        context = self.get_context(mail_list)
        self.append('echo %(object_id)i $(list_members %(list_name)s | wc -l)' % context)
    
    def get_context(self, mail_list):
        return {
            'list_name': mail_list.name,
            'object_id': mail_list.pk,
        }