#!/usr/bin/env python
# -*- coding: utf-8 -*-
##
#
# Vaisala software source code file
#
# Copyright (c) Vaisala Oyj 2015. All rights reserved.
#
##
"""
Tool for reconfiguring PostgreSQL.
"""
from __future__ import print_function

import argparse
import codecs
import datetime
import grp
import logging
import os
import pwd
import shutil
import sys

MANDATORY_OPTIONS = {
    "shared_buffers": "1024MB",
    "work_mem": "10MB",
    "maintenance_work_mem": "128MB",
    "checkpoint_segments": "16",
    "checkpoint_completion_target": "0.9",
    "effective_cache_size": "2GB",
    "autovacuum": "on",
    "default_statistics_target": "100",
    "log_line_prefix": "'%t <s[%s] u[%u] d[%d] r[%r] p[%p] i[%i]> '",
    "log_timezone": "'UTC'",
    "log_statement": "'ddl'",
    "timezone": "'UTC'",
    "data_directory": "'/srv/vaisala/radarsw/pgsql/9.4/data'"
}

logger = logging.getLogger('rsw-postgresql-configure-settings')

def back_up_current_conf(file_path):
    now = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    backup_file = file_path + "." + now
    shutil.copyfile(file_path, backup_file)
    logger.debug(u"'{}' backed up as '{}'.".format(file_path, backup_file))

def move_temp_to_conf(from_file, to_file):
    os.rename(from_file, to_file)
    logger.debug(u"'{}' moved to '{}'.".format(from_file, to_file))

def assign_permissions(file_path):
    read_and_write_permissions = 0o600
    user = pwd.getpwnam("postgres")[2]
    group = grp.getgrnam("postgres")[2]

    os.chown(file_path, user, group)
    os.chmod(file_path, read_and_write_permissions)
    logger.debug(u"Permissions for '{}' set to {} and owned by {}.".format(
        file_path, repr(read_and_write_permissions), repr((user, group))))

def remove_temp(file_path):
    os.unlink(file_path)
    logger.debug(u"File '{}' removed.".format(file_path))

def main():
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument("--postgresql-conf-file", dest="postgresql_conf_file",
                        metavar="FILE")
    group.add_argument("--test", dest="test", action="store_true",
                       help="Test that this tool is executable.")
    parser.add_argument("-d", "--debug", dest="debug", action="store_true",
                        help="Enable debug logging.")
    args = parser.parse_args()

    if args.test:
        sys.exit(0)

    config_file = args.postgresql_conf_file
    temp_file = config_file + '.tmp'

    def setup_logging(debug_enabled):
        root = logging.getLogger()
        if debug_enabled:
            root.setLevel(logging.DEBUG)
        else:
            root.setLevel(logging.INFO)
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        ch.setFormatter(formatter)
        root.addHandler(ch)
    setup_logging(args.debug)

    with codecs.open(config_file, 'r') as f:
        config_lines = [line.replace('\n', '') for line in f.readlines()]

    new_lines = 0
    missing_mandatory_keys = set(MANDATORY_OPTIONS.keys())
    with codecs.open(temp_file, 'w') as f:
        logger.debug("Writing new PostgreSQL configuration to '{}'.".format(temp_file))
        for line in config_lines:
            if line.strip().startswith("#"):
                print(line, file=f)
                continue

            parts = [part.strip() for part in line.strip().split('#')[0].strip().split('=')]
            if len(parts) == 1:
                print(line, file=f)
                continue

            key = parts[0]
            value = parts[1]

            if key in MANDATORY_OPTIONS:
                missing_mandatory_keys.remove(key)
                if value != MANDATORY_OPTIONS[key]:
                    print(u"# {}".format(line), file=f)
                    print(u"{} = {}".format(key, MANDATORY_OPTIONS[key]), file=f)
                    new_lines += 1
                    logger.info(u"# {:<30} {:<20} =>    {:<20}".format(key, value, MANDATORY_OPTIONS[key]))
                else:
                    print(line, file=f)
                    continue
            else:
                print(line, file=f)
                continue

        if len(missing_mandatory_keys) > 0:
            print("", file=f)
        for key in missing_mandatory_keys:
            print(u"{} = {}".format(key, MANDATORY_OPTIONS[key]), file=f)
            new_lines += 1
            logger.info(u"# {:<30} {:<20} =>    {:<20}".format(key, "MISSING", MANDATORY_OPTIONS[key]))

    if new_lines > 0:
        assign_permissions(temp_file)
        back_up_current_conf(config_file)
        move_temp_to_conf(temp_file, config_file)
        logger.info("PostgreSQL configuration changed. " +
                    "Restart the service to take modifications into use.")
    else:
        remove_temp(temp_file)
        logger.info("PostgreSQL configuration not changed.")


if __name__ == '__main__':
    main()
