# -*- coding: utf-8 -*-

import os
import jinja2
import sys
import json
import inspect
import locale
import smtplib
import traceback
from contextlib import contextmanager

from email.header import Header
from email.message import Message
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.Utils import formatdate
try:
    import misaka
    def markdown(text):
        return misaka.html(text, misaka.EXT_TABLES)
except ImportError:
    from markdown import markdown
from locale_util import setlocale

if '/usr/scripts' not in sys.path:
    sys.path.append('/usr/scripts')
from gestion import secrets_new as secrets

default_language = 'fr'
template_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'template') + '/'
html_template = template_path + 'html'
html_mutilang_template = template_path + 'html_multilang'
text_mutilang_template = template_path + 'text_multilang'

templateLoader = jinja2.FileSystemLoader( searchpath=["/", template_path]  )
templateEnv = jinja2.Environment( loader=templateLoader )

def format_date(d):
    """ Renvoie une jolie représentation (unicode) d'un datetime"""
    # L'encoding dépend de ce qu'on a choisi plus bas
    lang, encoding = locale.getlocale()
    if not encoding:
        encoding = 'ascii'
    if lang == 'fr_FR':
        return d.strftime('%A %d %B %Y').decode(encoding)
    else:
        return d.strftime('%A, %B %d %Y').decode(encoding)

def given_name(adh):
    """Renvoie le joli nom d'un adhérent"""
    if 'club' in adh['objectClass']:
        return u'Club %s' % unicode(adh['nom'][0])
    return unicode(adh['prenom'][0]) + " " + unicode(adh['nom'][0])

templateEnv.filters['date'] = format_date
templateEnv.filters['name'] = given_name


# file extension to rendering function map
markup = {
  'md' : markdown,
  'html' : lambda x:x,
}

### For an example:
### print generate('bienvenue', {'From':'respbats@crans.org', 'To':'admin@genua.fr', 'lang_info':'English version below'}).as_string()
### or from a shell : python -c "import mail; print mail.generate('bienvenue', {'From':'respbats@crans.org', 'To':'admin@genua.fr', 'lang_info':'English version below'})"

def submessage(payload, type, charset='utf-8'):
    """Renvois un sous message à mettre dans un message multipart"""
    submsg = MIMEText('', type, charset)
    del(submsg['Content-Transfer-Encoding'])
#    submsg['Content-Transfer-Encoding'] = '8bit'
#    submsg['Content-Disposition'] = 'inline'
    submsg.set_payload(payload)
    return submsg

def get_lang(mail, part, lang, lang_fallback):
    """Récupère le chemin vers le fichier à utiliser, en fonction de la
    langue souhaitée"""
    for l in [lang, lang_fallback]:
        for ext in markup.keys():
            if os.path.isfile(template_path + mail + '/' + part + '/' + l + '.' + ext):
                return l, ext, template_path + mail + '/' + part + '/' + l + '.' + ext
        if os.path.isfile(template_path + mail + '/' + part + '/' + l):
            return l, None, template_path + mail + '/' + part + '/' + l
    raise ValueError("Language %s nor %s found" % (lang, lang_fallback))

def gen_local_body(fname, params, lang):
    """Génère le texte localisé d'un body"""
    locales = {
        'fr': 'fr_FR.UTF-8',
        'en': 'en_US.UTF-8',
    }
    with setlocale(locales.get(lang, 'C')):
        return templateEnv.get_template(fname).render(params)

def body(mail, lang1, lang2, mk, params, charset):
    """Génère le texte du mail, en deux langues, avec une extension `mk` donnée
    """
    ret = []
    file1 = template_path + mail + '/body/' + lang1
    file2 = template_path + mail + '/body/' + lang2
    if mk:
        file1 = file1 + '.' + mk
        file2 = file2 + '.' + mk
    if lang1 == lang2 or not os.path.isfile(file2): # No alt language
        txt = gen_local_body(file1, params, lang1)
        if mk != "html":
            ret.append(submessage(txt.encode(charset), 'plain', charset))
        if mk: # compute the html version
            html = templateEnv.get_template(html_template).render({'body': markup[mk](txt)})
            ret.append(submessage(html.encode(charset), 'html', charset))
    else:
        txt1 = gen_local_body(file1, params, lang1)
        txt2 = gen_local_body(file2, params, lang2)
        if mk != "html":
            params_txt = dict(params)
            params_txt.update({'body1': txt1, 'body2': txt2})
            txt = templateEnv.get_template(text_mutilang_template).render(params_txt)
            ret.append(submessage(txt.encode(charset), 'plain', charset))
        if mk: # compute the html version
            params_html = dict(params)
            params_html.update({
                'lang1':lang1,
                'lang2':lang2,
                'body1': markup[mk](txt1),
                'body2': markup[mk](txt2),
            })
            html = templateEnv.get_template(html_mutilang_template).render(params_html)
            ret.append(submessage(html.encode(charset), 'html', charset))
    return ret

def generate(mail, params, lang=default_language, lang_fallback=default_language, lang_alt='en', charset='utf-8'):
    """Génère un message multipart"""

    if 'mailer' not in params:
        # Retrouve le nom du fichire ayant fait l'appel à la fonction
        frm = inspect.stack()[1]
        mod = inspect.getmodule(frm[0])
        if mod:
            params['mailer'] = os.path.abspath(mod.__file__)
        else:
            params['mailer'] = 'unknown file (%s)' % os.getlogin()

    msg = MIMEMultipart('mixed')
    inline_msg = MIMEMultipart('alternative')
    if os.path.isdir(template_path + mail):
        for filename in [dir for dir in os.listdir(template_path + mail) if os.path.isdir(template_path + mail + '/' + dir)]:
            lang_tmp, mk, file = get_lang(mail, filename, lang, lang_fallback)

            if filename == 'body':
                for part in body(mail, lang_tmp, lang_alt, mk, params, charset):
                    inline_msg.attach(part)
            else:
                txt = templateEnv.get_template(file).render(params)
                if filename in ['From', 'To', 'Cc', 'Bcc']:
                    msg[filename] = format_sender(txt, charset)
                else:
                    msg[filename] = Header(txt.encode(charset), charset)
        msg['Date'] = formatdate(localtime=True)
        msg.attach(inline_msg)

    return msg


def validation_url(view_name, data='', debug=False):
    """Enregistre une nouvelle url pour le module "validation" de l'intranet."""
    import requests
    HOST = os.getenv('DBG_INTRANET', 'intranet.crans.org')
    CA = '/etc/ssl/certs/cacert.org.pem'
    ROOT = 'https://%s' % HOST
    url = ROOT + '/validation/register/%s/' % view_name
    payload = {
        'data': json.dumps(data),
        'shared_secret': secrets.get('validation'),
    }
    req = requests.post(url, payload, verify=CA)
    if req.status_code != 200:
        raise Exception('Bad code status %d' % req.status_code)

    return ROOT + req.text.encode('utf-8')

def format_sender(sender, header_charset='utf-8'):
    """
    Check and format sender for header.
    """
    from email.Header import Header
    from email.Utils import parseaddr, formataddr

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)

    # We must always pass Unicode strings to Header, otherwise it will
    # use RFC 2047 encoding even on plain ASCII strings.
    sender_name = str(Header(unicode(sender_name), header_charset))

    # Make sure email addresses do not contain non-ASCII characters
    sender_addr = sender_addr.encode('ascii')

    return formataddr((sender_name, sender_addr))

@contextmanager
def bugreport():
    """Context manager: Si erreur, renvoie un bugreport avec un traceback à
    roots@."""
    try:
        yield
    except Exception as exc:
        From = 'roots@crans.org'
        to = From
        tb = sys.exc_info()[2].tb_next

        data = {
            'from': From,
            'to': to,
            'exc': exc,
            'lineno': tb.tb_frame.f_lineno,
            'filename': os.path.basename(tb.tb_frame.f_code.co_filename),
            'traceback': traceback.format_exc(),
        }
        mail = generate('bugreport', data)
        with ServerConnection() as conn:
            conn.sendmail(From, [to], mail.as_string())
        raise

class ServerConnection(object):
    """Connexion au serveur smtp"""
    _conn = None
    def __enter__(self):
        self._conn = smtplib.SMTP('smtp.adm.crans.org')
        return self

    def sendmail(self, From, to, mail):
        """Envoie un mail"""
        if os.getenv('DBG_MAIL', False):
            deb = os.getenv('DBG_MAIL')
            if '@' in deb:
                to = [deb]
            else:
                print mail
                return
        self._conn.sendmail(From, to, mail)

    def send_template(self, tpl_name, data):
        """Envoie un mail à partir d'un template.
        `data` est un dictionnaire contenant entre
        """
        From = data.get('from', '')
        adh = data.get('adh', data.get('proprio', ''))
        to = data.get('to', None) or (adh.get_mail() if adh else None)
        if to is None:
            print "No valid recipient mail. Aborting."
            return
        # TODO: get lang toussa
        body = generate(tpl_name, data).as_string()
        self.sendmail(From, to, body)


    def __exit__(self, type, value, traceback):
        self._conn.quit()

# TODO: intégrer ceci dans le ServerConnection
def postconf(i):
    "Fixe la fréquence d'envoi maximale par client (en msg/min)"
    os.system("/usr/sbin/postconf -e smtpd_client_message_rate_limit=%s" % i)
    os.system("/etc/init.d/postfix reload")

#    opt = commands.getoutput("/usr/sbin/postconf smtpd_client_message_rate_limit")
