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

import ConfigParser
import argparse
import collections
import logging
import platform
import subprocess
import sys

from os import path
from os import environ as env


logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
logger = logging.getLogger('color-scale-db-tool')


DATABASE_PACKAGE_LIB_DIR = "/usr/vaisala/radarsw/database/lib"
DEFAULT_CONFIG_DIRECTORY = env.get("VAISALA_RADARSW_CONFIG_DIR", '/etc/vaisala/radarsw/configuration')


DBConfig = collections.namedtuple('DBConfig', ['username', 'password', 'database'])


class DbToolException(Exception):
    pass


def handle_error(step_description, tpl):
    """Error handler and reporter used in calling external executables.

    Args:
        step_description: A human-readable description of the step being
            executed fit for inclusion into an error message, in the form of
            'creating user' or 'dropping database'.
        tpl: A four-tuple consisting of 
            (command executed,
             command return code,
             stdout as string,
             stderr as string); basically something _run_as_superuser returns.
    """
    command, retcode, out, err = tpl
    if retcode == 0:
        return

    logger.error(u"""Command output:
----
{}
----
Command error output:
----
{}
----""".format(out, err))

    raise DbToolException(
        u"Encountered error while {0}. Command exited with code {1}.".format(
            step_description, retcode
        )
    )


def run_as_superuser(sql, database="postgres"):
    """Execute SQL as database super-user. Works on both Windows and Linux.

    Args:
        sql: SQL to execute as string.
        database: Database to run the SQL in. Default is 'postgres' as this
            tool is mainly used for managing databases.

    Returns:
        A four-tuple of
            (command executed,
             command return code,
             stdout as string,
             stderr as string)
    """
    if platform.system() == 'Linux':
        cmd = ['su', '-', 'postgres', '-c',
               "psql -t -v ON_ERROR_STOP=1 -d " + database + "; exit $?"]
        logger.info(u"Attempting to run {0}".format(' '.join(cmd)))
        p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out_data, err_data = p.communicate(sql)
        return cmd, p.returncode, out_data, err_data
    elif is_windows():
        cmd = ['java', '-jar', 'jx-migrationtool.jar', '--sql', '"' + sql + '"', '--url',
               "jdbc:postgresql://localhost:5432/" + database + "?user=postgres&password=postgres"]
        logger.info(u"Attempting to run {0}".format(' '.join(cmd)))
        p = subprocess.Popen(cmd,
                             stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out_data, err_data = p.communicate(sql)
        return cmd, p.returncode, out_data, err_data


def is_windows():
    return platform.system() == 'Windows' or \
        "cygwin" in platform.system().lower()


def read_config(config_directory=DEFAULT_CONFIG_DIRECTORY):
    """Reads in configuration INI files such as:

    [DATASOURCE]
    datasource.url = jdbc:postgresql://localhost:5432/db_name
    datasource.username = user
    datasource.password = password

    Returns:
        A DBConfig instance.
    """
    override_config_file = path.join(config_directory, 'vsoweb-override.ini')
    default_config_file = path.join(config_directory, 'default', 'vsoweb-default.ini')
    configs = [override_config_file, default_config_file]

    if any(map(path.isfile, configs)) is not True:
        raise DbToolException(
            u"No valid configuration files found, tried {0}".format(
                ", ".join(configs)))

    username, password, database = None, None, None
    for config_path in configs:
        if not path.isfile(config_path):
            continue

        logger.debug("Reading config file %s" % config_path)
        config = ConfigParser.RawConfigParser()
        config.read(config_path)

        username_option = 'DATASOURCE', 'datasource.username'
        if username is None and config.has_option(*username_option):
            username = config.get(*username_option)

        password_option = 'DATASOURCE', 'datasource.password'
        if password is None and config.has_option(*password_option):
            password = config.get(*password_option)

        url_option = 'DATASOURCE', 'datasource.url'
        if database is None and config.has_option(*url_option):
            db_url = config.get(*url_option)
            # See http://jdbc.postgresql.org/documentation/head/connect.html for details.
            if '/' in db_url:
                database = db_url.split('/')[-1]
            else:
                database = db_url.split(':')[-1]

    if password is None:
        raise DbToolException("No password found for database!")

    result = DBConfig(username=username, password=password, database=database)
    logger.info("Using database configuration: %s" % repr(result))
    return result


def reset_color_scales_cli(db_config):
    sql = 'SELECT reset_color_scales();'
    return run_as_superuser(sql, db_config.database)


def reset_color_scales(db_config):
    if env.get('COLOR_SCALE_RECREATE', None) != 'yes':
        print("Run with COLOR_SCALE_RECREATE environment variable set to yes", file=sys.stderr)
        print("  In Linux, try COLOR_SCALE_RECREATE=yes %s reset-color-scales" % sys.argv[0], file=sys.stderr)
        print("  In Windows/Powershell, try & { $env:COLOR_SCALE_RECREATE = 'yes'; %s reset-color-scales }" %
              sys.argv[0],
              file=sys.stderr)
        sys.exit(1)
    return handle_error("resetting color scales to defaults", reset_color_scales_cli(db_config))


def main():
    parser = argparse.ArgumentParser('color-scale-db-tool',
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    subparsers = parser.add_subparsers(title="Commands", description="Supported commands")
    reset_color_scales_parser = subparsers.add_parser('reset-color-scales', help='Reset color scales.')
    reset_color_scales_parser.set_defaults(func=reset_color_scales)

    args = parser.parse_args()
    args.func(read_config())


if __name__ == '__main__':
    main()
