#! /usr/bin/python

from __future__ import print_function

import atexit
import bz2
from collections import defaultdict
import json
import lzma
from optparse import OptionParser
import requests
import shutil
import sys
import tempfile

import apt_pkg
from launchpadlib.launchpad import Launchpad

import lputils


tempdir = None


def ensure_tempdir():
    global tempdir
    if not tempdir:
        tempdir = tempfile.mkdtemp(prefix="unsubscribed-packages")
        atexit.register(shutil.rmtree, tempdir)


def decompress_open(tagfile):
    if tagfile.startswith("http:") or tagfile.startswith("ftp:"):
        url = tagfile
        tagfile = requests.get(url)
        if tagfile.status_code == 404:
            url = url.replace(".xz", ".bz2")
            tagfile = requests.get(url)

    ensure_tempdir()
    decompressed = tempfile.mktemp(dir=tempdir)
    with open(decompressed, "wb") as fout:
        if url.endswith(".xz"):
            fout.write(lzma.decompress(tagfile.content))
        elif url.endswith(".bz2"):
            fout.write(bz2.decompress(tagfile.content))
    return open(decompressed, "r")


def archive_base(archtag):
    if archtag in ("amd64", "i386", "src"):
        return "http://archive.ubuntu.com/ubuntu"
    else:
        return "http://ports.ubuntu.com/ubuntu-ports"


def source_names(options):
    sources = dict()
    for suite in options.suites:
        for component in ["main", "restricted"]:
            url = "%s/dists/%s/%s/source/Sources.xz" % (
                archive_base("src"), suite, component)
            if not options.quiet:
                print("Reading %s ..." % url, file=sys.stderr)
            for section in apt_pkg.TagFile(decompress_open(url)):
                pkg = section["Package"]
                if suite == options.dev_suite:
                    sources[pkg] = True
                else:
                    if sources.get(pkg, False) == True:
                        continue
                    sources[pkg] = False
    return sources


def main():
    parser = OptionParser(
        description="Check for source packages in main or restricted in "
                    "active distro series and return a json file of the teams "
                    "to which they map.")
    parser.add_option(
        "-l", "--launchpad", dest="launchpad_instance", default="production")
    parser.add_option(
        "-u", "--unsubscribed", action="store_true", default=False,
        help="Only return packages which have no subscriber")
    parser.add_option(
        "-p", "--print", action="store_true", default=False,
        dest="display",
        help="Print results to screen instead of a json file")
    parser.add_option(
        "-o", "--output-file", default="package-team-mapping.json",
        help="output JSON to this file")
    parser.add_option(
        "-q", "--quiet", action="store_true", default=False,
        help="Quieten progress messages")
    options, _ = parser.parse_args()
    options.suite = None
    options.distribution = "ubuntu"
    options.launchpad = Launchpad.login_with(
        "unsubscribed-packages", options.launchpad_instance)
    launchpad = options.launchpad
    ubuntu = launchpad.distributions[options.distribution]
    options.suites = []
    for series in ubuntu.series:
        # very few lucid packages are supported
        if series.name == 'lucid':
            continue
        if series.active:
            options.suites.append(series.name)
        # find the dev series
        if series.status in ['Active Development', 'Pre-release Freeze']:
            options.dev_suite = series.name

    lputils.setup_location(options)

    team_names = [
        'checkbox-bugs',
        'desktop-packages',
        'documentation-packages',
        'foundations-bugs',
        'kernel-packages',
        'kubuntu-bugs',
        'landscape',
        'maas-maintainers',
        'mir-team',
        'pkg-ime',
        'snappy-dev',
        'translators-packages',
        'ubuntu-openstack',
        'ubuntu-printing',
        'ubuntu-security',
        'ubuntu-server',
        ]

    data = { "unsubscribed": [] }
    subscriptions = defaultdict(list)
    for team_name in team_names:
        data[team_name] = []
        team = launchpad.people[team_name]
        team_subs = team.getBugSubscriberPackages()
        for src_pkg in team_subs:
            subscriptions[src_pkg.name].append(team_name)
            data[team_name].append(src_pkg.name)

    source_packages = source_names(options)
    for source_package in sorted(source_packages):
        # we only care about ones people are not subscribed to in the dev release
        if source_package not in subscriptions and source_packages[source_package]:
            data["unsubscribed"].append(source_package)
            if options.display:
                print("No team is subscribed to: %s" %
                      source_package)
        else:
            if not options.unsubscribed:
                if options.display:
                    print("%s is subscribed to: %s" %
                          (team_name, source_package))

    if not options.display:
        with open(options.output_file, 'w') as json_file:
            json_file.write(json.dumps(data, indent=4))


if __name__ == '__main__':
    main()
