from math import floor
from itertools import groupby
from operator import itemgetter

class Flakiness(object):
    def __init__(self, testRun, testRuns, queryParameters, startDate, endDate):
        self._testRun = testRun
        self._testRuns = testRuns
        self._queryParameters = queryParameters
        self._startDate = startDate
        self._endDate = endDate

    def _get_result(self, r):
        return r == 'OK' or r == 'PASSED'

    def _edges(self, results):
        e = 0
        v = results[0]
        for r in results:
            if r != v:
                e += 1
                v = r
        return e

    def getFlakinessResult(self):
        numberOfTestsToShow = int(self._queryParameters.get('testsToShow') or 10)
        prefix = self._queryParameters.get('prefix') or ''

        by_last_modified = itemgetter(0)

        found_import_events = self._testRuns.getEventsBetween(self._startDate.getTime(), self._endDate.getTime(), {'testSpecification': self._testRun.getTestSpecificationName(), 'type': 'importStarted'})
        testrun_vs_lastmodified = [(event.get('lastModified'), event.get('runId')) for event in found_import_events ]
        testrun_vs_lastmodified.sort()
        testrun_vs_lastmodified = testrun_vs_lastmodified[-30:]

        by_name = itemgetter(0)
        by_run_id = itemgetter(1)
        by_result = itemgetter(2)
        by_event = itemgetter(3)

        found_events = self._testRuns.getEventsBetween(self._startDate.getTime(), self._endDate.getTime(), {'testSpecification': self._testRun.getTestSpecificationName(), 'type': 'functionalResult'})
        quadruplets = [(event.get('name'), event.get('runId'), self._get_result(event.get('result')), event) for event in found_events if event.get('name').startswith(prefix)]
        quadruplets.sort()

        tests = []
        for name, group in groupby(quadruplets, by_name):
            # build array: passed (True), failed (False), nothing (None)
            run_id_to_result = { by_run_id(e) : {'result': by_result(e),
                                                 'duration': by_event(e).get('duration'),
                                                 'firstError': by_event(e).get('firstError') if by_event(e).hasProperty('firstError') else ''} for e in group }
            # order the results on the time axis (most recent first, since testrun_vs_lastmodified is ordered as such)
            # results in a list of True, False, None
            results = [run_id_to_result.get(run_id) for _, run_id in testrun_vs_lastmodified]
            flakiness_results = [ r['result'] for r in results if r ]
            flakiness = 0
            if len(flakiness_results) > 0:
                flakiness = floor(100.0 * self._edges(flakiness_results) / len(flakiness_results))

            name = name.split(';')
            tests.append({'suiteName': '.'.join(name[:-1]), 'label': name[-1], 'flakiness': flakiness, 'results': results})

        tests.sort(key=itemgetter('flakiness'), reverse=True)
        return {
            'tests': tests[:numberOfTestsToShow],
            'dates': map(by_last_modified, testrun_vs_lastmodified),
            'resultsLength': len(testrun_vs_lastmodified)
        }
