#!/usr/bin/python3
# SPDX-License-Identifier: Apache-2.0

import enarxbot
import datetime

class Override(Exception):
    def __init__(self, responsible):
        self.responsible = responsible

def merge_events_and_reviews(pr):
    "Gets a summary of all events on a PR by merging issue events and reviews."
    def inner(pr):
        for e in pr.get_issue_events():
            if e.event not in ("review_requested", "review_request_removed"):
                continue

            yield {
                "when": e.created_at,
                "what": e.event.upper(),
                "who": e.requested_reviewer.login
            }

        for r in pr.get_reviews():
            now = datetime.datetime.now()
            yield {
                "when": r.submitted_at if r.state != 'PENDING' else now,
                "what": r.state,
                "who": r.user.login
            }

    return sorted(inner(pr), key=lambda x: x["when"])

def get_responsible(pr):
    "Yields the users responsible for a PR or raises Override(responsible)."

    # Our strategy in this function is to reverse iterate through the events
    # of each PR participant trying to find the most recent event that assigns
    # responsibility. Some event types assign responsiblity. Others do not
    # assign responsibility. Still others modify the effects of earlier events.
    # The last event which assigns responsibility is the one that we choose.

    events = merge_events_and_reviews(pr)
    participants = {x["who"] for x in events} - {pr.user.login}
    segregated = {u: [x["what"] for x in events if x["who"] == u] for u in participants}

    for (who, whats) in segregated.items():
        unrequested = False
        for what in reversed(whats):
            if what == "REVIEW_REQUESTED" and not unrequested:
                yield who
            elif what in ("DISMISSED", "PENDING"):
                yield who
            elif what == "CHANGES_REQUESTED":
                raise Override({pr.user.login})
            elif what == "APPROVED":
                break # Neither PR author nor reviewer are responsible.
            else:
                if what == "REVIEW_REQUEST_REMOVED":
                    unrequested = True
                continue
            break

for issue in enarxbot.connect().search_issues(f"org:enarx is:pr is:public is:open -is:draft"):
    slug = f"{issue.repository.organization.login}/{issue.repository.name}#{issue.number}"
    pr = issue.as_pull_request()

    try:
        responsible = set(get_responsible(pr))
    except Override as e:
        responsible = set(e.responsible)

    assignees = {a.login for a in pr.assignees}

    remove = assignees - responsible
    needed = responsible - assignees

    if "dependabot[bot]" in needed:
        needed.remove("dependabot[bot]")

    print(f"{slug}:", end='')
    for u in needed:
        print(f" +{u}", end='')
    for u in remove:
        print(f" -{u}", end='')
    print()

    if remove:
        enarxbot.remove_from_assignees(pr, remove)
    if needed:
        enarxbot.add_to_assignees(pr, needed)
