#!/usr/bin/env python
""" update_apk_description.py

    Update the descriptions of an application (multilang)
    Example of beta update:
    $ python update_apk_description.py --service-account foo@developer.gserviceaccount.com --package-name org.mozilla.firefox --credentials key.p12 --update-apk-description
"""
import sys
import os
import urllib2
import json

from oauth2client import client

# load modules from parent dir
sys.path.insert(1, os.path.dirname(sys.path[0]))

# import the guts
from mozharness.base.script import BaseScript
from mozharness.mozilla.googleplay import GooglePlayMixin
from mozharness.base.python import VirtualenvMixin


class UpdateDescriptionAPK(BaseScript, GooglePlayMixin, VirtualenvMixin):
    all_actions = [
        'create-virtualenv',
        'update-apk-description',
        'test',
    ]

    default_actions = [
        'create-virtualenv',
        'test',
    ]

    config_options = [
        [["--service-account"], {
            "dest": "service_account",
            "help": "The service account email",
        }],
        [["--credentials"], {
            "dest": "google_play_credentials_file",
            "help": "The p12 authentication file",
            "default": "key.p12"
        }],
        [["--l10n-api-url"], {
            "dest": "l10n_api_url",
            "help": "The L10N URL",
            "default": "https://l10n.mozilla-community.org/~pascalc/stores_l10n/",
        }],
        [["--store"], {
            "dest": "store",
            "help": "Name of the store",
        }],
        [["--channel"], {
            "dest": "channel",
            "help": "Firefox channel",
        }],
        [["--force-locale"], {
            "dest": "force_locale",
            "help": "Force a specific locale (instead of all)",
        }],
    ]

    packages = {
        "google": {  # Google play
            "aurora": "org.mozilla.fennec_aurora",
            "beta": "org.mozilla.firefox_beta",
            "release": "org.mozilla.firefox"
        },
        # not supported yet
        "apple": {}
    }

    def __init__(self, require_config_file=False, config={},
                 all_actions=all_actions,
                 default_actions=default_actions):

        # Default configuration
        default_config = {
            'debug_build': False,
            'pip_index': True,
            # this will pip install it automajically when we call the
            # create-virtualenv action
            'virtualenv_modules': ['google-api-python-client'],
            "find_links": [
                "http://pypi.pvt.build.mozilla.org/pub",
                "http://pypi.pub.build.mozilla.org/pub",
            ],
            'virtualenv_path': 'venv',
        }
        default_config.update(config)

        BaseScript.__init__(
            self,
            config_options=self.config_options,
            require_config_file=require_config_file,
            config=default_config,
            all_actions=all_actions,
            default_actions=default_actions,
        )
        self.api_url = self.config['l10n_api_url'] + "api/" + self.config['store']
        self.all_locales_url = self.api_url + "/done/{channel}/"
        self.locale_url = self.api_url + "/translation/{channel}/{locale}"
        self.mapping_url = self.api_url + "/localesmapping/?reverse"

    def check_argument(self):
        """ Check that the given values are correct,
        files exists, etc
        """

        if self.config['store'] not in self.packages.keys():
            self.fatal("Unknown store value " +
                       self.config['store'])

        packages = self.packages[self.config['store']]
        if self.config['channel'] not in packages.keys():
            self.fatal("Unknown channel value " +
                       self.config['channel'])

        self.package_name = packages[self.config['channel']]

        if self.package_name not in packages.values():
            self.fatal("Unknown package name value " +
                       self.package_name)

        if not os.path.isfile(self.config['google_play_credentials_file']):
            self.fatal("Could not find " + self.config['google_play_credentials_file'])

    def get_list_locales(self, channel):

        """ Get all the translated locales supported by Google play
        So, locale unsupported by Google play won't be downloaded
        Idem for not translated locale
        """
        URL = self.all_locales_url.format(channel=channel)
        try:
            response = urllib2.urlopen(URL)
        except urllib2.HTTPError:
            self.fatal("Could not download the locale list. Channel: '" + channel + "', URL: '" + self.all_locales_url + "'")
        try:
            json_content = json.load(response)
        except ValueError, e:
            self.fatal("Could not parse the JSON content: '" + str(e) + "' / URL: '" + URL + "'")
        return json_content

    def get_mapping(self):
        """ Download and load the locale mapping
        """
        try:
            response = urllib2.urlopen(self.mapping_url)
        except urllib2.HTTPError:
            self.fatal("Could not download the locale mapping list. URL: '" + self.mapping_url + "'")
        self.mappings = json.load(response)

    def locale_mapping(self, locale):
        """ Google play and Mozilla don't have the exact locale code
        Translate them
        """
        if locale in self.mappings:
            return self.mappings[locale]
        else:
            return locale

    def get_translation(self, channel, locale):
        """ Get the translation for a locale
        """
        localeURL = self.locale_url.format(channel=channel, locale=locale)
        try:
            response = urllib2.urlopen(localeURL)
        except urllib2.HTTPError:
            self.fatal("Could not download the locale translation. Locale: '" + locale + "', Channel: '" + channel + "', URL: '" + localeURL + "'")
        return json.load(response)

    def update_desc(self, service, package_name, channel):
        """ Update the desc on google play

        service -- The session to Google play
        package_name -- The name of the package
        channel -- The channel (aurora, beta, etc)
        locale -- The locale to update
        description -- The new description
        """

        edit_request = service.edits().insert(body={},
                                              packageName=package_name)
        result = edit_request.execute()
        edit_id = result['id']

        # Retrieve the mapping
        self.get_mapping()

        if self.config.get("force_locale"):
            # The user forced a locale, don't need to retrieve the full list
            locales = [self.config.get("force_locale")]
        else:
            # Get all the locales from the web interface
            locales = self.get_list_locales(channel)
        nb_locales = 0
        for locale in locales:
            translation = self.get_translation(channel, locale)
            title = translation.get("title")
            short_desc = translation.get("short_desc")
            long_desc = translation.get("long_desc")

            # Google play expects some locales codes (de-DE instead of de)
            locale = self.locale_mapping(locale)

            try:
                self.log("Updating " + package_name + " for '" + locale +
                         "' /  title: '" + title + "', short_desc: '" +
                         short_desc[0:20] + "'..., long_desc: '" +
                         long_desc[0:20] + "...'")
                listing_response = service.edits().listings().update(
                    editId=edit_id, packageName=package_name, language=locale,
                    body={'fullDescription': long_desc,
                          'shortDescription': short_desc,
                          'title': title}).execute()
                nb_locales += 1
            except client.AccessTokenRefreshError:
                self.log('The credentials have been revoked or expired,'
                         'please re-run the application to re-authorize')

        # Commit our changes
        commit_request = service.edits().commit(
            editId=edit_id, packageName=package_name).execute()
        self.log('Edit "%s" has been committed. %d locale(s) updated.' % (commit_request['id'], nb_locales))

    def update_apk_description(self):
        """ Update the description """
        self.check_argument()
        service = self.connect_to_play()
        self.update_desc(service, self.package_name, self.config['channel'])

    def test(self):
        """
        Test if the connexion can be done and if the various method
        works as expected
        """
        self.check_argument()
        self.connect_to_play()
        channel = 'beta'
        locales = self.get_list_locales(channel)
        if not locales:
            self.fatal("get_list_locales() failed")

        self.get_mapping()
        if not self.mappings:
            self.fatal("get_mapping() failed")

        loca = self.locale_mapping("fr")
        if loca != "fr-FR":
            self.fatal("fr locale_mapping failed")
        loca = self.locale_mapping("hr")
        if loca != "hr":
            self.fatal("hr locale_mapping failed")

        translation = self.get_translation(channel, 'cs')
        if len(translation.get('title')) < 5:
            self.fatal("get_translation title failed for the 'cs' locale")
        if len(translation.get('short_desc')) < 5:
            self.fatal("get_translation short_desc failed for the 'cs' locale")
        if len(translation.get('long_desc')) < 5:
            self.fatal("get_translation long_desc failed for the 'cs' locale")

        channel = "beta"
        translation = self.get_translation(channel, 'fr')
        if len(translation.get('title')) < 5:
            self.fatal("get_translation title failed for the 'fr' locale")
        if len(translation.get('short_desc')) < 5:
            self.fatal("get_translation short_desc failed for the 'fr' locale")
        if len(translation.get('long_desc')) < 5:
            self.fatal("get_translation long_desc failed for the 'fr' locale")

        channel = "release"
        translation = self.get_translation(channel, 'de')
        if len(translation.get('title')) < 5:
            self.fatal("get_translation title failed for the 'de' locale")
        if len(translation.get('short_desc')) < 5:
            self.fatal("get_translation short_desc failed for the 'de' locale")
        if len(translation.get('long_desc')) < 5:
            self.fatal("get_translation long_desc failed for the 'de' locale")

# main {{{1
if __name__ == '__main__':
    myScript = UpdateDescriptionAPK()
    myScript.run_and_exit()
