#!/usr/bin/env python3
import ast
import datetime
import logging
import os
import pathlib
import signal
import sys
import time
import urllib.parse
import webbrowser
from argparse import ArgumentParser

import dateparser
import htmlmin
import yaml

from telemeter_reporter import SLIReporter

logger = logging.getLogger("telemeter-reporter")


# Handle Ctrl-C
def signal_handler(sig, frame):
    print('Received SIGINT. Exiting...')
    sys.exit(0)


signal.signal(signal.SIGINT, signal_handler)

# Handle command line args
arg_parser = ArgumentParser(description="Tool for generating reports on SLA/SLO compliance using "
                                        "Telemeter-LTS data", )

arg_parser.add_argument("-c", "--config",
                        help="Path to YAML file containing configuration data. Default: "
                             "~/.telemeter_reporter.yml", metavar="PATH")
arg_parser.add_argument("output", help="Destination path for the generated report (- = stdout)")
format_choices = ['simple', 'plain', 'html', 'csv', 'grid', 'fancy_grid', 'github', 'jira', 'latex']
arg_parser.add_argument("-f", "--format", default=None, metavar="FMT", choices=format_choices,
                        help="Format for the report. Can be provided multiple times (see --auto-ext"
                             "). Options: {}. Default: simple".format(str(format_choices)),
                        action='append')
arg_parser.add_argument("-u", "--uhc-query", metavar='QUERY',
                        help="Report on all clusters returned by this query to the UHC API")
arg_parser.add_argument("-t", "--title", metavar='TITLE', help="Optional title for HTML reports")
arg_parser.add_argument("-i", "--time", metavar='TIME',
                        help="Generate a report at a certain point in the past. Strings like '2 "
                             "weeks ago', 'last Monday', 'yesterday at 8pm', or 'October 1, 2018' "
                             "are all acceptable")
arg_parser.add_argument("-b", "--no-browser", action="store_true",
                        help="Don't open the resulting report in a web browser (if HTML report is "
                             "selected)")
arg_parser.add_argument("-a", "--auto-ext", action="store_true",
                        help="Automatically append a file extension onto the provided output "
                             "path. Enabled by default when --format is used multiple times. Has "
                             "no effect when output = stdout.")
arg_parser.add_argument("-n", "--no-duration-adjust", action="store_true",
                        help="Disable automatic duration adjustment. By default, any user-defined "
                             "'duration' global query var is overridden with the cluster age if "
                             "duration > cluster age. Clusters triggering this adjustment will "
                             "have an asterisk appended to their name. This flag will disable "
                             "this behavior.")
arg_parser.add_argument("-m", "--minify", action="store_true", help="Minify HTML output")
arg_parser.add_argument("-p", "--parents", action="store_true",
                        help="Same behavior as mkdir's --parents option. Creates parent "
                             "directories in the output path if necessary.")
log_choices = ['critical', 'error', 'warning', 'info', 'debug']
arg_parser.add_argument("-l", "--log", default='warning', metavar='LEVEL', choices=log_choices,
                        help="Set the verbosity/logging level. Options: {}".format(log_choices))
arg_parser.add_argument("-o", "--override", metavar='VARS',
                        help="Override global variables set in the configuration file. Provide a "
                             "valid Python dict string, e.g. \"{'duration': 28}\"")
args = arg_parser.parse_args()

# Set logging level
logging.basicConfig(level=args.log.upper())

# Load config
if args.config is not None:
    config_path = os.path.expanduser(args.config)
    logger.info("Loading config from {} (set via -c flag)".format(config_path))
elif os.getenv('TELEMETER_REPORTER_CONFIG'):
    config_path = os.path.expanduser(os.getenv('TELEMETER_REPORTER_CONFIG'))
    logger.info(
        "Loading config from {} (set via TELEMETER_REPORTER_CONFIG env-var)".format(config_path))
else:
    config_path = os.path.expanduser("~/.telemeter_reporter.yml")
    logger.info("Loading config from {} (by default)".format(config_path))

with open(config_path, 'r') as f:
    config = yaml.safe_load(f)

# Override global vars
if args.override:
    try:
        config['global_vars'].update(ast.literal_eval(args.override))
    except AttributeError:
        config['global_vars'] = ast.literal_eval(args.override)

# Override environmental vars for secrets
if os.getenv('TELEMETER_TOKEN'):
    config['api']['telemeter']['token'] = os.getenv('TELEMETER_TOKEN')
if os.getenv('UHC_TOKEN'):
    config['api']['uhc']['token'] = os.getenv('UHC_TOKEN')

# Correct default format
if args.format is None:
    args.format = ['simple']

# Create SLIReporter instance
elapsed = time.perf_counter()
sli_rep = SLIReporter(config)

# Parse time argument
if args.time:
    report_time = dateparser.parse(args.time, settings={'RETURN_AS_TIMEZONE_AWARE': True})
    if not report_time:
        logger.fatal("Failed to parse time argument. Exiting...")
        sys.exit(-3)
else:
    report_time = None

# Get Cluster external_ids
if args.uhc_query:
    clusters = sli_rep.get_clusters(args.uhc_query, report_time)
else:
    clusters = []
    for uhc_query in config['clusters']:
        clusters += sli_rep.get_clusters(uhc_query, report_time)

# Do the actual queries (this may take a while...)
raw_report = sli_rep.generate_report(clusters, query_time=report_time,
                                     adjust_duration=not args.no_duration_adjust)
elapsed = time.perf_counter() - elapsed

# Format the report
for fmt in args.format:
    if fmt == "html":
        # For HTML reports, we force color on add a title
        if args.title:
            title = args.title
        else:
            try:
                today = datetime.date.today() if not report_time else report_time.date()
                start = today - datetime.timedelta(days=int(config['global_vars']['duration']))
                title = "SLO Report: {} to {} ".format(start.isoformat(), today.isoformat())
            except KeyError:
                title = "SLO Report"
        footer = "Report generated {} in {:.2f} sec".format(
            datetime.datetime.now(datetime.timezone.utc).strftime("%F %T %Z"), elapsed)
        headers = sli_rep.generate_headers(html_tooltips=True)
        formatted_report = sli_rep.format_report(headers=headers, raw_report=raw_report, fmt=fmt,
                                                 color=True, title=title, footer=footer)
    elif fmt == "csv":
        # For CSV reports, we provide essentially raw data: no rounding, no percent signs, no color
        headers = sli_rep.generate_headers()
        formatted_report = sli_rep.format_report(headers=headers, raw_report=raw_report, fmt=fmt,
                                                 color=False)
    elif fmt in ['github', 'jira', 'latex']:
        # For other markup languages, we output the same data shown in the "simple" format, just
        # just without any color or newlines added to the headers
        headers = sli_rep.generate_headers()
        formatted_report = sli_rep.format_report(headers=headers, raw_report=raw_report, fmt=fmt,
                                                 color=False)
    else:
        # For all other formats, we line-break every header row at each space to reduce width, and
        # only enable color if we're printing to stdout
        headers = [x.replace(' ', '\n') for x in sli_rep.generate_headers()]
        formatted_report = sli_rep.format_report(headers=headers, raw_report=raw_report, fmt=fmt,
                                                 color=(args.output == '-'))
    # Minify HTML
    if args.minify and fmt == "html":
        formatted_report = htmlmin.minify(formatted_report, remove_comments=True,
                                          remove_empty_space=True)

    # Output report
    if args.output == '-':
        print(formatted_report)
    else:
        save_path = os.path.abspath(os.path.expanduser(
            "{}.{}".format(args.output, fmt) if args.auto_ext or len(
                args.format) > 1 else args.output))
        if args.parents:
            pathlib.Path(save_path).parent.mkdir(parents=True, exist_ok=True)

        with open(save_path, 'w') as f:
            f.write(formatted_report)
        if fmt == "html" and not args.no_browser:
            webbrowser.open_new_tab("file://" + urllib.parse.quote(save_path))
