import subprocess

import mit


class MailingList(object):
    def __init__(self, name, ):
        self.name = name

    def list_members(self, ):
        raise NotImplementedError

    def change_members(self, add_members, delete_members, ):
        raise NotImplementedError


BLANCHE_PATH="/usr/bin/blanche"
class MoiraList(MailingList):
    def __init__(self, *args, **kwargs):
        super(MoiraList, self).__init__(*args, **kwargs)
        self._ccache = None

    @property
    def ccache(self, ):
        if not self._ccache:
            self._ccache = mit.kinit()
        return self._ccache

    def list_members(self, ):
        env = dict(KRB5CCNAME=self.ccache.name)
        res = subprocess.Popen(
            [BLANCHE_PATH, self.name, ],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=env,
        )
        stdout, stderr = res.communicate()
        if res.returncode:
            raise RuntimeError("Failed to list members: %s" % (stderr, ))
        members = [self.convert_moira_to_email(m) for m in stdout.strip().split("\n")]
        return members

    def convert_moira_to_email(self, email):
        typ, colon, val = email.partition(":")
        if typ in ('STRING', 'USER', 'LIST'):
            email = val + "@mit.edu"
        # Leave untouched: KERBEROS (shouldn't exist), untagged strings
        assert email.count('@') == 1, "%s has wrong number of @'s" % (email, )
        return email

    def strip_mit(self, email):
        if '@' in email:
            local, domain = email.split('@')
            if domain.lower() == 'mit.edu':
                return local
        return email

    def canonicalize_member(self, member):
        if type(member) == type(()):
            name, email = member
        else:
            name = None
            email = member
        email = self.strip_mit(email)
        return name, email

    def change_members(self, add_members, delete_members, ):
        """
        Add and/or remove members from the list.
        """

        # Note that it passes all members on the commandline, so it shouldn't be
        # used for large lists at the moment. OTOH, "large" appears to be
        # 2M characters, so.
        # If that becomes an issue, it should probably check the number of
        # changes, and use -al / -dl with a tempfile as appropriate.

        env = dict(KRB5CCNAME=self.ccache.name)
        cmdline = [BLANCHE_PATH, self.name, ]

        for member in add_members:
            name, email = self.canonicalize_member(member)
            if name:
                cmdline.extend(('-at', email, name))
            else:
                cmdline.extend(('-a', email))

        for member in delete_members:
            name, email = self.canonicalize_member(member)
            cmdline.extend(('-d', email))

        res = subprocess.Popen(
            cmdline,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            env=env,
        )
        stdout, stderr = res.communicate()
        return stdout



MMBLANCHE_PATH="/mit/consult/bin/mmblanche"
class MailmanList(MailingList):
    def list_members(self, ):
        res = subprocess.Popen(
            [MMBLANCHE_PATH, self.name, ],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        stdout, stderr = res.communicate()
        if res.returncode:
            raise RuntimeError("Failed to list members: %s" % (stderr, ))
        members = stdout.strip().split("\n")
        return members

    def change_members(self, add_members, delete_members, ):
        """
        Add and/or remove members from the list.
        """

        # Note that it passes all members on the commandline, so it shouldn't be
        # used for large lists at the moment. OTOH, "large" appears to be
        # 2M characters, so.
        # If that becomes an issue, it should probably check the number of
        # changes, and use -al / -dl with a tempfile as appropriate.

        cmdline = [MMBLANCHE_PATH, self.name, ]
        for member in add_members:
            cmdline.append('-a')
            if type(member) == type(()):
                name, email = member
                name = name.replace('"', "''")
                member = '"%s" <%s>' % (name, email, )
            cmdline.append(member)
        for member in delete_members:
            cmdline.append('-d')
            if type(member) == type(()):
                name, member = member
            cmdline.append(member)
        res = subprocess.Popen(
            cmdline,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        stdout, stderr = res.communicate()
        assert stderr=="", ("stderr unexpectedly non-empty: %s" % (stderr, ))
        return stdout
