import os
import sys

from rldjango.apps.crm.models import Lead, CRMTag, Communication, Role, Language, EmailJob, LinkAddress
from mailer.models import Message, DontSendEntry, MessageLog
from mailer.lockfile import FileLock, AlreadyLocked, LockTimeout

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.mail.message import EmailMultiAlternatives
from django.core.management.base import BaseCommand, CommandError
from django.template import Context, Template

from optparse import make_option

import base64
from reportlab.lib import arciv
import re
try:
    from collections import namedtuple
except ImportError:
    from rlextra.utils.namedtuple import NamedTuple as namedtuple

LOCK_WAIT_TIMEOUT = getattr(settings, "MAILER_LOCK_WAIT_TIMEOUT", -1)

LinkInfo = namedtuple('LinkInfo','id short_check_code')

_plain_re = re.compile('(http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?)')
_html_re = re.compile(' href=[\'"](http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?)[\'"]')

class Command(BaseCommand):
    option_list = BaseCommand.option_list
    help = 'Prepares the emails for each lead and makes Message objects'
    args = ''

    def handle(self, *app_labels, **options):
        lock = FileLock("send_mail")
        try:
            lock.acquire(LOCK_WAIT_TIMEOUT)
        except AlreadyLocked:
            print 'send_mail', 'is locked already.'
            return
        except LockTimeout:
            print 'send_mail', 'can\'t be locked.'
            return
        verbosity = int(options['verbosity'])

        dontSend = [x.to_address for x in DontSendEntry.objects.all()]
        ML = {}     #list mapping email address to list of subjects sent
        for message_log in MessageLog.objects.all():
            ML.setdefault(",".join(message_log.to_addresses),set()).add(message_log.subject.strip())

        try:
            email_jobs = EmailJob.objects.filter(queued=False)
            if verbosity:
                print '##### no of unqueued jobs=%s' % len(email_jobs)

            for job in email_jobs:
                leads = Lead.objects.filter(tags=job.tag.id)
                ignore_previously_emailed = job.ignore_previously_emailed
                ignore_previously_emailed_with_same_subject = job.ignore_previously_emailed_with_same_subject
                subject = job.communication.subject.strip()
                if verbosity:
                    print '##### lead tag=%s number of leads=%s subject=%r' % (job.tag,len(leads),subject)

                #filter out unwanted cases
                new_leads = [].append
                for lead in leads:
                    email = lead.email.strip()
                    if (email in dontSend
                         or (email in ML
                             and (ignore_previously_emailed
                                 or ignore_previously_emailed_with_same_subject and subject in ML[email]))):
                            continue
                    new_leads(lead)
                    ML.setdefault(email,set()).add(subject)

                leads = new_leads.__self__
                del new_leads

                #preprocess URLs
                LD = {}
                job_message_plain = job.communication.message_plain
                plain_urls = [url[0] for url in _plain_re.findall(job_message_plain)]
                job_message_html = job.communication.message_html
                html_urls = [url[0] for url in _html_re.findall(job_message_html)]
                for url in plain_urls+html_urls:
                    try:
                        link = LinkAddress.objects.get(address=url)
                    except:
                        link = LinkAddress(address=url)
                        link.save()
                    if url not in LD:
                        LD[url] = LinkInfo(link.id,link.short_check_code())

                for lead in leads:
                    email = ''
                    CRM_SEND_EMAILS = getattr(settings, 'CRM_SEND_EMAILS', False)
                    if isinstance(CRM_SEND_EMAILS, list):
                        for domain in CRM_SEND_EMAILS:
                            if re.search('@%s$' % domain, lead.email):
                                email = lead.email
                        if not email:
                            email = 'nobody@example.com'
                    elif CRM_SEND_EMAILS:
                        email = lead.email
                    else:
                        email = 'nobody@example.com'

                    dynamic_data = dict([(x.name,getattr(lead,x.name)) for x in lead._meta.fields if getattr(lead,x.name,None) is not None])

                    # Create an ID to be used in the unsubscribe link
                    key = settings.SECRET_KEY
                    unsubscribe_id_arciv = arciv.encode(str(email), key)
                    unsubscribe_id_base16 = base64.b16encode(unsubscribe_id_arciv)
                    dynamic_data['unsubscribe_url'] = 'http://%s/admin/crm/unsubscribe/%s' % (settings.HOSTNAME, unsubscribe_id_base16)

                    # Tracking images
                    dynamic_data['tracking_image'] = '<img src="http://%s/imgid/%s%s/%s%s/" width="1" height="1" />' % (settings.HOSTNAME, job.communication.id, job.communication.short_check_code(), lead.id, lead.short_check_code())

                    c = Context(dynamic_data, autoescape=False)

                    #Replace all http://... URLs with short tracking redirect links from LinkAddress model

                    message_plain = job_message_plain
                    for url in plain_urls:
                        message_plain = message_plain.replace(url, 'http://%s/redirect/%s%s/%s%s/%s%s/' % (settings.HOSTNAME, job.communication.id, job.communication.short_check_code(), lead.id, lead.short_check_code(),  LD[url].id, LD[url].short_check_code))
                    t = Template(message_plain)
                    message_body = t.render(c)

                    message_html = job_message_html
                    for url in html_urls:
                        message_html = message_html.replace(url, 'http://%s/redirect/%s%s/%s%s/%s%s/' % (settings.HOSTNAME, job.communication.id, job.communication.short_check_code(), lead.id, lead.short_check_code(), LD[url].id, LD[url].short_check_code))
                    t = Template(message_html)
                    message_html = t.render(c)

                    #unsubscribe_url = 'http://%s/crm/unsubscribe/%s' % (request.get_host(), unsubscribe_id_base16)
                    #message_body += '\n\n\nTo unsubscribe, go to this URL: %s' % (unsubscribe_url)

                    msg = EmailMultiAlternatives(job.communication.subject,
                            message_body,
                            job.created_by.email if job.created_by else settings.CRM_DEFAULT_FROM_EMAIL,
                            email.split(), # 'to' must be a list
                            )
                    msg.attach_alternative(message_html, "text/html")
                    db_msg = Message()#priority=priority)
                    db_msg.email = msg
                    db_msg.save()
                    if verbosity>=3:
                        print '----- saving mailer.Message for %s' % lead.email

                job.queued = True
                job.save()

        finally:
            lock.release()
