#!/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 sys
import argparse
import filecmp
import os
import datetime
import shutil
import pwd
import grp
import codecs
import logging

from os import environ as env
from os import path

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'"
}

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()
