# Copyright 2019 Vitaliy Zakaznikov (TestFlows Test Framework http://testflows.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import threading

from datetime import datetime

import testflows.settings as settings
import testflows._core.cli.arg.type as argtype

from testflows._core.flags import Flags, SKIP
from testflows._core.testtype import TestType
from testflows._core.transform.log.message import message_map
from testflows._core.cli.arg.common import epilog
from testflows._core.cli.arg.common import HelpFormatter
from testflows._core.cli.arg.handlers.handler import Handler as HandlerBase
from testflows._core.transform.log.pipeline import ResultsLogPipeline
from testflows._core.transform.log.message import FailResults, XoutResults
from testflows._core.utils.timefuncs import localfromtimestamp, strftimedelta

testflows = '<span class="testflows-logo"></span> [<span class="logo-test">Test</span><span class="logo-flows">Flows</span>]'
testflows_em = testflows.replace("[", "").replace("]", "")

chart_script = """
window.onload = function() {
    window.char = new Chart(document.getElementById("MyChart"), {
        type: 'bar',
        data: {
          labels: [2017, 2018, 2019, 2020, 2021, 2022, 2023],
          datasets: [{
            label: "Pass",
            type: "bar",
            stack: "passing",
            backgroundColor: "#5ab57b94",
            data: [30, 31, 32, 33, 34, 35, 36],
          }, {
            label: "Fail",
            type: "bar",
            stack: "failing",
            backgroundColor: "#e937376e",
            data: [15, 16, 17, 18, 19, 20, 21],
          }, {
            label: "Known",
            type: "bar",
            stack: "known",
            backgroundColor: "#307bbb6b",      
            data: [20, 21, 22, 23, 24, 25, 26],
          }] 
        },
        options: {
          scales: {
            xAxes: [{
              stacked: true,
              ticks: {
                beginAtZero: true,
                maxRotation: 0,
                minRotation: 0
              }
            }],
            yAxes: [{
              stacked: true,
            }]
          },
        }
      });
};
"""
template = f"""
# Comparison Report

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<canvas id="MyChart" width="400" height="200"></canvas>
<script>
{chart_script}
</script>

%(body)s
  
---
Generated by {testflows} Open-Source Test Framework

[<span class="logo-test">Test</span><span class="logo-flows">Flows</span>]: https://testflows.com
"""

class Formatter:
    def format_table(self, data):
        table = data["table"]
        s = "\n"
        # reference table
        s += " | ".join(table["reference"]["header"]) + "\n"
        s += " | ".join(["---"] * len(table["reference"]["header"])) + "\n"
        for row in table["reference"]["rows"]:
            s += " | ".join(row) + "\n"

        # comparison table
        s += "\n"
        s += " | ".join(table["header"]) + "\n"
        s += " | ".join(["---"] * len(table["header"])) + "\n"
        span = '<span class="result result-%(cls)s">%(name)s</span>'
        for row in table["rows"]:
            name, *results = row
            s += " | ".join([name] + [
                span % {'cls': result.lower(), 'name': result} for result in results
            ]) + "\n"
        return s

    def format(self, data):
        body = self.format_table(data)
        return template.strip() % {"body": body}

class Handler(HandlerBase):
    @classmethod
    def add_command(cls, commands):
        parser = commands.add_parser("compare", help="comparison report", epilog=epilog(),
            description="Generate comparison report between test runs.",
            formatter_class=HelpFormatter)

        parser.add_argument("--log", metavar="pattern", type=argtype.file("r", bufsize=1, encoding="utf-8"),
            nargs="+", help="log file pattern", required=True)
        parser.add_argument("--only", metavar="pattern", nargs="+",
            help="compare only selected tests", type=str, required=False)
        parser.add_argument("--order-by", metavar="name", type=str,
            help="attribute that is used to order the logs")
        parser.add_argument("--sort", metavar="direction", type=str,
            help="sort direction. Either 'asc' or 'desc', default: asc", choices=["asc", "desc"], default="asc")
        parser.add_argument("--format", metavar="type", type=str,
            help="output format, default: md (Markdown)", choices=["md"], default="md")
        parser.add_argument("output", metavar="output", type=argtype.file("w", bufsize=1, encoding="utf-8"),
            nargs="?", help='output file, default: stdout', default="-")

        parser.set_defaults(func=cls())

    def tests(self, results):
        tests = []
        for r in results.values():
            for test in r["tests"].values():
                if test["test"].p_type < TestType.Test:
                    continue
                tests.append(test["test"].name)
        return sorted(list(set(tests)))

    def table(self, tests, results):
        table = {
            "header": ["Test Name"] + [f'<a href="#ref-{i + 1}">[{i + 1}]</a>' for i, r in enumerate(results)],
            "rows": [],
            "reference": {
                "header": ["Reference", "File"],
                "rows": [[f'<span id="ref-{i + 1}"><strong>[{i + 1}]</strong></span>', ref] for i, ref in enumerate(results.keys())]
            },
        }
        for test in tests:
            row = [test]
            for result in results.values():
                if result["tests"].get(test):
                    row.append(result["tests"].get(test)["result"].name)
                else:
                    row.append("-")
            table["rows"].append(row)
        return table

    def data(self, results):
        d = dict()
        d["list"] = self.tests(results)
        d["table"] = self.table(d["list"], results)
        return d

    def generate(self, formatter, results, args):
        output = args.output
        output.write(formatter.format(self.data(results)))
        output.write("\n")

    def handle(self, args):
        results = {}
        threads = []

        def thread_worker(log, results):
            ResultsLogPipeline(log, results).run()

        for log in args.log:
            log_results = {}
            threads.append(
                threading.Thread(target=thread_worker, args=(log, log_results))
            )
            results[log.name] = log_results
            threads[-1].start()

        for thread in threads:
            thread.join()

        formatter = Formatter()
        self.generate(formatter, results, args)
