#!/usr/bin/env python3
# Copyright (c) 2022 Canonical Ltd.
# Author: Dariusz Gadomski <dgadomski@ubuntu.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
# the full text of the license.


import argparse
import datetime
import io
import json
import logging
import os
import subprocess
import sys
import tempfile

from gtimelog.timelog import TimeCollection, TimeLog, format_duration_short, as_minutes
from timelog.weekly import (gt_file, virtual_midnight, set_logfile, set_userid,
                            parse_date, lookahead)
from timelog.tl import Categories, TimelogRc


LOG = logging.getLogger(__name__)


FILL_OUTPUT_FILENAME = "#FILLME#"


def get_time():
    today = datetime.datetime.today()
    return today, today + datetime.timedelta(days=1)


def round_duration(x, base=5):
    return base * round(x/base)


def date_range(start_date, end_date):
    for n in range(int((end_date - start_date).days)):
        yield start_date + datetime.timedelta(n)


def extract_date(timelogrc, timestampstr):
    return datetime.datetime.strptime(timestampstr, timelogrc.get_date_format_json()).strftime(timelogrc.get_date_format_filename())


def check_entries(entries):
    fd, filename = tempfile.mkstemp(prefix="timelog_upload_",
                                    suffix=".json",
                                    text=True)

    with open(filename, 'w+') as tempf:
        tempf.write(json.dumps(entries, indent=4))

    # TODO: check if its possible to use external editor from inside a snap
    EDITOR = "vim.tiny"

    mtime_before = os.path.getmtime(filename)
    subprocess.call([EDITOR, "-u", "NONE", filename])
    mtime_after = os.path.getmtime(filename)

    output = []

    if mtime_after > mtime_before:
        # file was saved in editor, proceed with upload
        with open(filename, "r") as tempf:
            data = tempf.read()
            output = json.loads(data)
    else:
        LOG.info("File not saved, exitting")

    return output


def extract_case_no(entry):
    if entry.startswith("SF#"):
        return entry.split()[0][3:]
    return template.replace("#TIMECARDS_JSON#", content.strip())


def get_case(timelogrc, category, entry):
    mapping = None
    if category.strip() == Categories['ua'].strip():
        mapping = extract_case_no(entry)
    else:
        # fallback to manual case mapping from ini
        mapping = timelogrc.get_case_mapping(category, entry)

    if not mapping:
        raise KeyError("no mapping for {}".format(category))
    return mapping.zfill(8)


def get_desc(category, entry):
    desc = "# TODO: {}".format(entry)
    if category.strip() != Categories['ua']:
        desc = entry
    return desc


def format_entry(timelogrc, category, start, entry, duration):
    return [
        timelogrc.get_sf_username(),            # SF_USER
        get_case(timelogrc, category, entry),              # case
        round_duration(as_minutes(duration)),   # minutes
        str(start.date().strftime(timelogrc.get_date_format_json())), # date
        str(start.time().strftime(timelogrc.get_time_format_json())),    # time
        get_desc(category, entry)               # description
    ]


def run_through_jq(json):
    with subprocess.Popen("jq @json", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process:
        process.stdin.write(json.encode())
        process.stdin.close()
        return process.stdout.read().decode()


def replace_template(timelogrc, content):
    template = ""
    template_filename = timelogrc.get_side_template()
    try:
        with open(template_filename, "r") as f:
            template = f.read()
    except FileNotFoundError:
        LOG.error("Can't open selenium IDE template file: {}".format(template_filename))

    return template.replace("#TIMECARDS_JSON#", content.strip())


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-l', '--logfile', nargs=1, metavar='LOGFILE',
                        help='Path to the gtimelog logfile to be use')
    parser.add_argument('-f', '--from-date',
                        help='Select the start date of the period to display')
    parser.add_argument('-t', '--to-date',
                        help='Select the end date of the period to display')
    parser.add_argument('-s', '--selenium',
                        dest='selenium',
                        const=FILL_OUTPUT_FILENAME,
                        default=None,
                        action='store',
                        nargs='?',
                        help='Produce a Selenium IDE file for automated timecard filing')
    args = parser.parse_args()

    rc = TimelogRc()

    (date_start, date_end) = get_time()
    if args.from_date:
        date_start = parse_date(args.from_date)
        date_end = date_start
    if args.to_date:
        date_end = parse_date(args.to_date)
    if date_start > date_end:
        LOG.error("Starting date should be less than the ending date.")
        parser.print_usage()
        raise SystemExit

    if args.logfile is not None:
        LogFile = set_logfile(args.logfile)
    else:
        LogFile = set_logfile()

    log = TimeLog(LogFile, virtual_midnight)

    tasks = []

    for day in date_range(date_start, date_end + datetime.timedelta(1)):
        log_entries = log.window_for_day(day)

        work,slack = log_entries.grouped_entries()
        for start, entry, duration in work:
            category, task = TimeCollection.split_category(entry)
            category = category.strip()

            tasks.append((start, category, task, duration))

    out = sys.stdout

    entries = []

    if len(tasks) > 0:
        for (start, category, task, duration), has_more in lookahead(tasks):
            entries.append(format_entry(rc,
                                        category,
                                        start,
                                        task,
                                        duration
                                        )
                           )

    if args.selenium:
        entries = check_entries(entries)
        if entries:
            entries_json = json.dumps(entries)
            jq_output = run_through_jq(entries_json)
            filled_template = replace_template(rc, jq_output)

            out_filename = args.selenium
            if args.selenium == FILL_OUTPUT_FILENAME:
                out_filename = "%s.side" % extract_date(rc, entries[-1][3])

            with open(out_filename, 'w+') as outf:
                outf.write(filled_template)

            print("Saved to %s" % out_filename)

        else:
            LOG.info("Not saved, exiting.")
    else:
        print(json.dumps(entries, indent=4))

