#!/usr/bin/env python3
import shlex
import csv
import os
import sys
import pathlib
from email import message_from_file
from email.message import Message
from email.policy import SMTPUTF8
from functools import partial
import re
import horetu
import horetu.types as T
from . import dump, util

def _as_identifier(name):
    return util.as_7bit(name) \
        .strip().replace('.', '').replace(' ', '.').lower()

def newsbeuter(fp):
    NAME = re.compile(r'^~([^(]+).*$')
    FEED = re.compile(r'^https?://')

    for line in fp:
        if re.match(FEED, line):
            name = None

            if ' ' in line:
                url, rest = line.split(' ', 1)
                for token in shlex.split(rest):
                    m = re.match(NAME, token)
                    if m:
                        name = m.group(1)
                        yield _as_identifier(name), {'name': name, 'feed': url}
                        break

def seltzer(fp):
    try:
        import lxml.html
    except ImportError:
        msg = 'lxml is not available. Install it with "pip3 install \'bguo[seltzer]\'".'
        raise horetu.Error(msg)
    keys = ('name', 'membership', 'email', 'phone')

    html = lxml.html.fromstring(fp.buffer.read())
    for tr in html.xpath('//table/tbody/tr'):
        values = (td.text_content().strip().replace('\r', '') for td in tr.xpath('td'))
        row = dict(zip(keys, values))
        row['name'] = re.sub(r'([^,]+), (.+)', r'\2 \1', row['name'])
        del(row['membership'])
        yield _as_identifier(row['name']), row

def _aliases(with_name, without_name, fp):
    for line in fp:
        m = re.match(with_name, line)
        if m:
            identifier, name, email = m.groups()
            yield identifier.strip(), {
                'name': name.strip(),
                'email': email.strip(),
            }
        else:
            m = re.match(without_name, line)
            if m:
                identifier, email = m.groups()
                yield identifier.strip(), {
                    'email': email.strip(),
                }

mh = partial(_aliases,
             re.compile(r'([^:]+): *([^<]+)<([^> ]*)>.*'),
             re.compile(r'([^:]+): *([^ <>]+)'))

mutt = partial(_aliases,
               re.compile(r'^alias ([^ ]+) +(.+) ([^ ]+)$'),
               re.compile(r'^alias ([^ ]+) +([^ ]+)$'))


_with_carrots = re.compile(r'^([^<]*) *<([^>]+)>$')
def email_from(fp):
    from_ = message_from_file(fp)['from'].strip()
    m_carrot = re.match(_with_carrots, from_)
    if m_carrot:
        name, email = m_carrot.groups()
    else:
        name = ''
        email = from_
    identifier = email.split('@')[0]
    yield identifier, {
        'name': name,
        'email': email,
    }

def voipms(fp):
    reader = csv.reader(fp)
    for speeddial, name, phone in reader:
        identifier = _as_identifier(name)
        yield identifier, {'name': name, 'phone': phone}

FORMATS = {
    'mh': mh,
    'mutt': mutt,
    'email': email_from,
    'voipms': voipms,
    'seltzer': seltzer,
    'newsbeuter': newsbeuter,
}
MODES = ('a', 'w')

def parse(input_format: FORMATS,
          input: T.InputFile=sys.stdin,
          dout: T.OutputDirectory='.',
          *, 
          dry=False):
    '''
    Convert data from a single file in a different format to a directory
    of bguo format files.

    :param input_format: "mh" to read an MH ~/.aliases file, "voipms" to
        read a CSV export from voip.ms, or "seltzer" to read an HTML
        file generated by the seltzer CRM members page ("/?q=members")
    :param input: Input file, or STDIN by default
    :param dout: Output directory, or the current directory by default
    :param bool dry: Write no files, but print what files would have been written.
    :param mode: "w" to write only new files, skipping existing files,
        or "a" to append to existing files
    '''
    for identifier, record in input_format(input):
        if not identifier:
            sys.stderr.write('No identifier for %s\n' % repr(record))
            continue

        filename = os.path.join(dout, identifier)
 
        if os.path.exists(filename):
            with open(filename) as fp:
                m = dump._message(fp.read())
        else:
            m = Message(policy=SMTPUTF8)

        for key in record:
            if key in m:
                if record[key].strip() not in set(v.strip() for v in m.get_all(key)):
                    if dry:
                        p = key, filename, record[key]
                        sys.stderr.write('Add %s to %s: %s\n' % p)
                    else:
                        m[key] = record[key]
            else:
                if dry:
                    p = key, filename, record[key]
                    sys.stderr.write('Add %s to %s: %s\n' % p)
                else:
                    m[key] = record[key]

        if not dry:
            text = str(m).strip().replace('\r', '') + '\n'
            with open(filename, 'w') as fout:
                fout.write(text)
