#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (C) 2007 Canonical Ltd., Steve Kowalik
# Authors:
#  Martin Pitt <martin.pitt@ubuntu.com>
#  Steve Kowalik <stevenk@ubuntu.com>
#  Michael Bienia <geser@ubuntu.com>
#  Daniel Hahler <ubuntu@thequod.de>
#  Iain Lane <laney@ubuntu.com>
#  Jonathan Davies <jpds@ubuntu.com>
#  Markus Korn <thekorn@gmx.de> (python-launchpadlib support)
#
# ##################################################################
#
# 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; version 2.
#
# 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.
#
# See file /usr/share/common-licenses/GPL-2 for more details.
#
# ##################################################################

import optparse
import os
import sys

from debian.changelog import Version
from distro_info import UbuntuDistroInfo

from ubuntutools.config import UDTConfig, ubu_email
from ubuntutools.lp import udtexceptions
from ubuntutools.misc import require_utf8
from ubuntutools.question import confirmation_prompt, EditBugReport

#
# entry point
#

def main():
    ubu_info = UbuntuDistroInfo()
    DEFAULT_SOURCE = 'unstable'
    if ubu_info.is_lts(ubu_info.devel()):
        DEFAULT_SOURCE = 'testing'

    # Our usage options.
    usage = ('Usage: %prog [options] '
             '<source package> [<target release> [base version]]')
    parser = optparse.OptionParser(usage)

    parser.add_option('-d', type='string',
                      dest='dist', default=DEFAULT_SOURCE,
                      help='Debian distribution to sync from.')
    parser.add_option('-k', type='string',
                      dest='keyid', default=None,
                      help='GnuPG key ID to use for signing report '
                           '(only used when emailing the sync request).')
    parser.add_option('-n', action='store_true',
                      dest='newpkg', default=False,
                      help='Whether package to sync is a new package in '
                           'Ubuntu.')
    parser.add_option('--email', action='store_true', default=False,
                      help='Use a PGP-signed email for filing the sync '
                           'request, rather than the LP API.')
    parser.add_option('--lp', dest='deprecated_lp_flag',
                      action='store_true', default=False,
                      help=optparse.SUPPRESS_HELP)
    parser.add_option('-l', '--lpinstance', metavar='INSTANCE',
                      dest='lpinstance', default=None,
                      help='Launchpad instance to connect to '
                           '(default: production).')
    parser.add_option('-s', action='store_true',
                      dest='sponsorship', default=False,
                      help='Force sponsorship')
    parser.add_option('-C', action='store_true',
                      dest='missing_changelog_ok', default=False,
                      help='Allow changelog to be manually filled in '
                           'when missing')
    parser.add_option('-e', action='store_true',
                      dest='ffe', default=False,
                      help='Use this after FeatureFreeze for non-bug fix '
                           'syncs, changes default subscription to the '
                           'appropriate release team.')
    parser.add_option('--no-conf', action='store_true',
                      dest='no_conf', default=False,
                      help="Don't read config files or environment variables")

    (options, args) = parser.parse_args()

    if not len(args):
        parser.print_help()
        sys.exit(1)

    require_utf8()

    config = UDTConfig(options.no_conf)

    if options.deprecated_lp_flag:
        print "The --lp flag is now default, ignored."
    if options.email:
        options.lpapi = False
    else:
        options.lpapi = config.get_value('USE_LPAPI', default=True,
                                         boolean=True)
    if options.lpinstance is None:
        options.lpinstance = config.get_value('LPINSTANCE')

    if not options.lpapi:
        if options.lpinstance == 'production':
            bug_mail_domain = 'bugs.launchpad.net'
        elif options.lpinstance == 'staging':
            bug_mail_domain = 'bugs.staging.launchpad.net'
        else:
            print >> sys.stderr, ('Error: Unknown launchpad instance: %s'
                                  % options.lpinstance)
            sys.exit(1)

    mailserver_host = config.get_value('SMTP_SERVER',
                                       default=None,
                                       compat_keys=['UBUSMTP', 'DEBSMTP'])
    if not options.lpapi and not mailserver_host:
        try:
            import DNS
            DNS.DiscoverNameServers()
            mxlist = DNS.mxlookup(bug_mail_domain)
            firstmx = mxlist[0]
            mailserver_host = firstmx[1]
        except ImportError:
            print >> sys.stderr, ('Please install python-dns to support '
                                  'Launchpad mail server lookup.')
            sys.exit(1)

    mailserver_port = config.get_value('SMTP_PORT', default=25,
                                       compat_keys=['UBUSMTP_PORT',
                                                    'DEBSMTP_PORT'])
    mailserver_user = config.get_value('SMTP_USER',
                                       compat_keys=['UBUSMTP_USER',
                                                    'DEBSMTP_USER'])
    mailserver_pass = config.get_value('SMTP_PASS',
                                       compat_keys=['UBUSMTP_PASS',
                                                    'DEBSMTP_PASS'])

    # import the needed requestsync module
    if options.lpapi:
        from ubuntutools.requestsync.lp import (check_existing_reports,
                                                get_debian_srcpkg,
                                                get_ubuntu_srcpkg,
                                                get_ubuntu_delta_changelog,
                                                need_sponsorship, post_bug)
        from ubuntutools.lp.lpapicache import Distribution, Launchpad
        # See if we have LP credentials and exit if we don't -
        # cannot continue in this case

        try:
            # devel for changelogUrl()
            Launchpad.login(service=options.lpinstance, api_version='devel')
        except IOError:
            sys.exit(1)
    else:
        from ubuntutools.requestsync.mail import (check_existing_reports,
                                                  get_debian_srcpkg,
                                                  get_ubuntu_srcpkg,
                                                  get_ubuntu_delta_changelog,
                                                  mail_bug, need_sponsorship)
        if not any(x in os.environ for x in ('UBUMAIL', 'DEBEMAIL', 'EMAIL')):
            print >> sys.stderr, (
                'E: The environment variable UBUMAIL, DEBEMAIL or EMAIL needs '
                'to be set to let this script mail the sync request.')
            sys.exit(1)

    newsource = options.newpkg
    sponsorship = options.sponsorship
    distro = options.dist
    ffe = options.ffe
    lpapi = options.lpapi
    need_interaction = False
    force_base_version = None
    srcpkg = args[0]

    if len(args) == 1:
        if lpapi:
            release = Distribution('ubuntu').getDevelopmentSeries().name
        else:
            release = ubu_info.devel()
        print >> sys.stderr, 'W: Target release missing - assuming %s' % release
    elif len(args) == 2:
        release = args[1]
    elif len(args) == 3:
        release = args[1]
        force_base_version = Version(args[2])
    else:
        print >> sys.stderr, 'E: Too many arguments.'
        parser.print_help()
        sys.exit(1)

    # Get the current Ubuntu source package
    try:
        ubuntu_srcpkg = get_ubuntu_srcpkg(srcpkg, release)
        ubuntu_version = Version(ubuntu_srcpkg.getVersion())
        ubuntu_component = ubuntu_srcpkg.getComponent()
        newsource = False # override the -n flag
    except udtexceptions.PackageNotFoundException:
        ubuntu_srcpkg = None
        ubuntu_version = Version('~')
        ubuntu_component = 'universe' # let's assume universe
        if not newsource:
            print ("'%s' doesn't exist in 'Ubuntu %s'.\n"
                   "Do you want to sync a new package?"
                   % (srcpkg, release))
            confirmation_prompt()
            newsource = True

    # Get the requested Debian source package
    try:
        debian_srcpkg = get_debian_srcpkg(srcpkg, distro)
        debian_version = Version(debian_srcpkg.getVersion())
        debian_component = debian_srcpkg.getComponent()
    except udtexceptions.PackageNotFoundException, error:
        print >> sys.stderr, "E: %s" % error
        sys.exit(1)

    # Stop if Ubuntu has already the version from Debian or a newer version
    if (ubuntu_version >= debian_version) and options.lpapi:
        # try rmadison
        import ubuntutools.requestsync.mail
        try:
            debian_srcpkg = ubuntutools.requestsync.mail.get_debian_srcpkg(
                    srcpkg, distro)
            debian_version = Version(debian_srcpkg.getVersion())
            debian_component = debian_srcpkg.getComponent()
        except udtexceptions.PackageNotFoundException, error:
            print >> sys.stderr, "E: %s" % error
            sys.exit(1)

    if ubuntu_version == debian_version:
        print  >> sys.stderr, ('E: The versions in Debian and Ubuntu are the '
                               'same already (%s). Aborting.'
                               % ubuntu_version)
        sys.exit(1)
    if ubuntu_version > debian_version:
        print >> sys.stderr, ('E: The version in Ubuntu (%s) is newer than '
                              'the version in Debian (%s). Aborting.'
                              % (ubuntu_version, debian_version))
        sys.exit(1)

    # -s flag not specified - check if we do need sponsorship
    if not sponsorship:
        sponsorship = need_sponsorship(srcpkg, ubuntu_component, release)

    if not sponsorship and not ffe:
        print >> sys.stderr, ('Consider using syncpackage(1) for syncs that '
                              'do not require feature freeze exceptions.')

    # Check for existing package reports
    if not newsource:
        check_existing_reports(srcpkg)

    # Generate bug report
    pkg_to_sync = ('%s %s (%s) from Debian %s (%s)'
                   % (srcpkg, debian_version, ubuntu_component,
                      distro, debian_component))
    title = "Sync %s" % pkg_to_sync
    if ffe:
        title = "FFe: " + title
    report = "Please sync %s\n\n" % pkg_to_sync

    if 'ubuntu' in str(ubuntu_version):
        need_interaction = True

        print ('Changes have been made to the package in Ubuntu.\n'
               'Please edit the report and give an explanation.\n'
               'Not saving the report file will abort the request.')
        report += (u'Explanation of the Ubuntu delta and why it can be '
                   u'dropped:\n%s\n>>> ENTER_EXPLANATION_HERE <<<\n\n'
                   % get_ubuntu_delta_changelog(ubuntu_srcpkg))

    if ffe:
        need_interaction = True

        print ('To approve FeatureFreeze exception, you need to state\n'
               'the reason why you feel it is necessary.\n'
               'Not saving the report file will abort the request.')
        report += ('Explanation of FeatureFreeze exception:\n'
                   '>>> ENTER_EXPLANATION_HERE <<<\n\n')

    if need_interaction:
        confirmation_prompt()

    base_version = force_base_version or ubuntu_version

    if newsource:
        report += 'All changelog entries:\n\n'
    else:
        report += ('Changelog entries since current %s version %s:\n\n'
                   % (release, ubuntu_version))
    changelog = debian_srcpkg.getChangelog(since_version=base_version)
    if not changelog:
        if not options.missing_changelog_ok:
            print >> sys.stderr, ("E: Did not retrieve any changelog entries. "
                                  "Do you need to specify '-C'? "
                                  "Was the package recently uploaded? (check "
                                  "http://packages.debian.org/changelogs/)")
            sys.exit(1)
        else:
            need_interaction = True
            changelog = "XXX FIXME: add changelog here XXX"
    report += changelog

    editor = EditBugReport(title, report)
    editor.edit(optional=not need_interaction)
    title, report = editor.get_report()

    if 'XXX FIXME' in report:
        print >> sys.stderr, ("E: changelog boilerplate found in report, "
                              "please manually add changelog when using '-C'")
        sys.exit(1)

    # bug status and bug subscriber
    status = 'confirmed'
    subscribe = 'ubuntu-archive'
    if sponsorship:
        status = 'new'
        subscribe = 'ubuntu-sponsors'
    if ffe:
        status = 'new'
        subscribe = 'ubuntu-release'

    srcpkg = not newsource and srcpkg or None
    if lpapi:
        # Map status to the values expected by LP API
        mapping = {'new': 'New', 'confirmed': 'Confirmed'}
        # Post sync request using LP API
        post_bug(srcpkg, subscribe, mapping[status], title, report)
    else:
        email_from = ubu_email(export=False)[1]
        # Mail sync request
        mail_bug(srcpkg, subscribe, status, title, report, bug_mail_domain,
                 options.keyid, email_from, mailserver_host, mailserver_port,
                 mailserver_user, mailserver_pass)

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print "\nUser abort."
        sys.exit(2)