#!/usr/bin/python3

import re
import smtplib
from email.mime.text import MIMEText
import os
import argparse
import datetime
import subprocess
import logging
from systemd import journal
from importlib import machinery, util


def load_config(name, filename):
    """
    DUPLICATE CODE. This function is also defined in pf-compare-pgextensions.
    Leave duplicate code here because the overhead for a python module is too
    high.
    Read a config from a py file with the given filename.
    Return config as a dictionary.
    """
    # Read config from file. This code is copied from python-perfact.generic
    loader = machinery.SourceFileLoader(name, filename)
    spec = util.spec_from_loader(loader.name, loader)
    mod = util.module_from_spec(spec)
    loader.exec_module(mod)

    return {
        name: getattr(mod, name)
        for name in dir(mod)
        if not name.startswith('_')
    }


# Set up logger
logger = logging.getLogger('etc-commit')
logger.addHandler(journal.JournalHandler())
logger.setLevel(logging.DEBUG)

PIPE = subprocess.PIPE

filename = '/etc/perfact/systemtools/autocommit-etc.py'
filename_custom = '/etc/perfact/systemtools/autocommit-etc-custom.py'

name = 'config'

ignore_files = []
ignore_files_custom = []
recipients_etc = []
if os.path.exists(filename):
    config = load_config(name, filename)
    ignore_files = config.get('ignore_files')
    recipients_etc = config.get('recipients_etc')

if os.path.exists(filename_custom):
    config_custom = load_config(name, filename_custom)
    ignore_files_custom = config_custom.get('ignore_files')
    ignore_files = ignore_files + ignore_files_custom
    if len(config_custom.get('recipients_etc')) > 0:
        recipients_etc = config_custom.get('recipients_etc')


def parse_arguments():
    parser = argparse.ArgumentParser(
        description=(
            "Check if there are uncommitted changes in the daily autocommit."
            "Send a mail if option -m is given."
        )
    )
    parser.add_argument(
        "--mail",
        "-m",
        help="Send a mail when uncommitted changes have been found",
        action="store_true"
    )
    parser.add_argument(
        "--sender",
        "-s",
        help="Define the sending mail address",
        default="no-reply@perfact.de",
    )
    parser.add_argument(
        "--date",
        "-d",
        help=(
            "Use another date to check for autocommit"
            " (default is the date of 'today')"
        ),
    )
    args = parser.parse_args()
    return args


def check_daily_commit(send_mail, recipient=None, sender=None, date=None,
                       ignore_files=[], recipients_etc=[]):
    '''Check if there are any files in the daily commit that are not in the
    ignore_files list.
    If param send_mail is set, send a mail to recipient.
    '''
    today = datetime.date.today()
    if date:
        try:
            date = datetime.datetime.strptime(date, '%Y-%m-%d')
        except Exception as e:
            print(e)
            msg = f'Wrong date format! Parsing failed. Defaulting to {today}'
            print(msg)
            logger.error(msg)
    if not date:
        date = today
    logger.info(f'Using date: {date}')
    # Get daily commit of today. Stop if nothing found
    git_cmd = ['git', 'log', '--since={} 00:00'.format(date),
               '--pretty=%H', '--grep=daily autocommit']
    os.chdir('/etc')
    process = subprocess.Popen(
        git_cmd,
        stdout=PIPE,
        stderr=PIPE,
        universal_newlines=True,
    )
    commit_hashes, _ = process.communicate()
    commit_hash = commit_hashes.split('\n')[0].strip()
    if not commit_hash:
        return
    logger.debug(f'Found daily autocommit with hash: {commit_hash}')

    # Get all files of commit
    git_cmd = [
        'git',
        'show',
        commit_hash,
        '--name-only',
        '--pretty=format:'
    ]
    process = subprocess.Popen(
        git_cmd,
        stdout=PIPE,
        stderr=PIPE,
     )
    files, _ = process.communicate()
    files = files.decode().strip().split('\n')

    # Check if there are files not in ignored_files
    report_commit = False
    for file in files:
        for ignore in ignore_files:
            match = re.match(
                ignore,
                file
            )
            if match:
                logger.debug(f'Ignoring file {file} based on pattern {ignore}')
                break
        else:
            report_commit = True
            logmsg = (
                'Uncommited changes in the following file: {}'
            ).format(file)
            logger.debug(logmsg)

    # Send a mail if we found a file and -m option is set
    if report_commit and send_mail:
        pfsystemid = open('/etc/pfsystemid').read().strip()
        pfsystemname = open('/etc/pfsystemname').read().strip()

        # report the whole commit
        git_cmd = ['git', 'show', '--name-status', commit_hash]
        process = subprocess.Popen(
            git_cmd,
            stdout=PIPE,
            stderr=PIPE,
        )
        status, _ = process.communicate()
        status = status.decode()
        msg = MIMEText(status, 'plain', 'utf-8')

        msg['Subject'] = 'Commit summary on {} ({})'.format(
            pfsystemname, pfsystemid
        )

        msg['From'] = sender

        msg['To'] = ', '.join(recipients_etc)

        smtp = smtplib.SMTP('localhost')
        smtp.sendmail(msg['From'], recipients_etc, msg.as_string())
        smtp.quit()


if __name__ == "__main__":
    args = parse_arguments()
    check_daily_commit(
        send_mail=args.mail,
        sender=args.sender,
        date=args.date,
        ignore_files=ignore_files,
        recipients_etc=recipients_etc,
    )
