#!/usr/bin/python3
# Copyright (C) 2019 Jelmer Vernooij <jelmer@jelmer.uk>
#
# 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.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

import logging

import json
import os

from typing import Optional, List

from debmutate.control import ControlEditor, delete_from_list
from debmutate.deb822 import ChangeConflict
from debmutate.reformatting import GeneratedFile, FormattingUnpreservable


BRANCH_NAME = "mia"
MIA_EMAIL = "mia@qa.debian.org"
MIA_TEAMMAINT_USERTAG = "mia-teammaint"


def versions_dict():
    import debmutate
    import debian
    return {
        'debmutate': debmutate.version_string,
        'debian': debian.__version__,
    }


class MIAResult(object):
    def __init__(
        self, source=None, removed_uploaders=None, bugs=None
    ):
        self.source = source
        self.removed_uploaders = removed_uploaders
        self.bugs = bugs


def all_mia_teammaint_bugs():
    import debianbts

    return set(
        debianbts.get_usertag(MIA_EMAIL, [MIA_TEAMMAINT_USERTAG])[MIA_TEAMMAINT_USERTAG]
    )


def get_package_bugs(source):
    import debianbts

    return set(debianbts.get_bugs(src=source, status="open"))


def get_mia_maintainers(bug) -> Optional[List[str]]:
    import debianbts

    log = debianbts.get_bug_log(bug)
    return log[0]["message"].get_all("X-Debbugs-CC")


def drop_uploaders(editor, mia_people):
    removed_mia = []
    try:
        uploaders = editor.source["Uploaders"].split(",")
    except KeyError:
        return []

    for person in mia_people:
        if person in [uploader.strip() for uploader in uploaders]:
            editor.source["Uploaders"] = delete_from_list(
                editor.source["Uploaders"], person
            )
            removed_mia.append(person)

    if not editor.source['Uploaders']:
        del editor.source['Uploaders']
    return removed_mia


def control_drop_mia_uploaders(editor):
    changelog_entries = []
    source = editor.source["Source"]
    bugs = all_mia_teammaint_bugs().intersection(get_package_bugs(source))
    if not bugs:
        return MIAResult()
    removed_uploaders = []
    fixed_bugs = []
    for bug in bugs:
        mia_people = get_mia_maintainers(bug)

        if mia_people is None:
            logging.warning(
                'No MIA people (X-Debbugs-CC) found in bug %d', bug)
            continue

        removed_mia = drop_uploaders(editor, mia_people)

        if len(removed_mia) == 0:
            continue

        if len(removed_mia) == 1:
            description = "Remove MIA uploader %s." % removed_mia[0]
        else:
            description = "Remove MIA uploaders %s." % (
                ", ".join(removed_mia)
            )
        if removed_mia == mia_people:
            description += " Closes: #%d" % bug
        changelog_entries.append(description)
        removed_uploaders.extend(removed_mia)
        fixed_bugs.append(bug)

    if not changelog_entries:
        return MIAResult(source, removed_uploaders=[], bugs=fixed_bugs)
    return MIAResult(
        source, removed_uploaders=removed_uploaders, bugs=fixed_bugs)


def report_fatal(code: str, description: str) -> None:
    if os.environ.get('SVP_API') == '1':
        with open(os.environ['SVP_RESULT'], 'w') as f:
            json.dump({
                'versions': versions_dict(),
                'result_code': code,
                'description': description}, f)
    logging.fatal('%s', description)


def main():
    import argparse
    parser = argparse.ArgumentParser()
    parser.parse_args()

    logging.basicConfig(level=logging.INFO, format='%(message)s')

    try:
        if os.path.exists('debian/debcargo.toml'):
            from debmutate.debcargo import DebcargoControlShimEditor, DebcargoEditor

            control = DebcargoControlShimEditor.from_debian_dir('debian')
        else:
            control = ControlEditor(path='debian/control')

        with control:
            result = control_drop_mia_uploaders(control)
    except FormattingUnpreservable as e:
        report_fatal(
            "formatting-unpreservable",
            "unable to preserve formatting while editing %s" % e.path,
        )
        return 1
    except (ChangeConflict, GeneratedFile) as e:
        report_fatal(
            "generated-file", "unable to edit generated file: %r" % e
        )
        return 1
    except FileNotFoundError as e:
        report_fatal(
            "missing-control-file",
            "Unable to find debian control file: %s" % e)
        return 1

    if not result.bugs:
        report_fatal("nothing-to-do", "No MIA people")
        return 0

    if not result.removed_uploaders:
        report_fatal(
            "nothing-to-do", "Unable to remove any MIA uploaders"
        )
        return 1

    if 'SVP_RESULT' in os.environ:
        with open(os.environ['SVP_RESULT'], 'w') as f:
            json.dump({
                'versions': versions_dict(),
                'description': 'Remove MIA uploaders',
                'context': {
                    'bugs': result.bugs,
                    'removed_uploaders': result.removed_uploaders,
                    }}, f)

    logging.info(
        'Dropped MIA uploaders (%r) from Uploaders.',
        result.removed_uploaders)


if __name__ == "__main__":
    import sys

    sys.exit(main())
