# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import logging
import os
import pbpack
import polib
import requests
import sys
import tempfile


logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True


LP_URLS = {"staging": "https://lang-packs-staging.getpebble.com",
           "production": "https://lp.getpebble.com"}
lp_url = None

if 'LP_HTTP_USERNAME' not in os.environ or 'LP_HTTP_PASSWORD' not in os.environ:
    print """Missing HTTP username/password!
Please define LP_HTTP_USERNAME and LP_HTTP_PASSWORD in your environment"""
    sys.exit(1)

lp_http_username = os.environ['LP_HTTP_USERNAME']
lp_http_password = os.environ['LP_HTTP_PASSWORD']
lp_auth = requests.auth.HTTPBasicAuth(lp_http_username, lp_http_password)

FIRMWARE_VERSION_TO_UPDATE = 4


def assert_ok(response):
    if response.status_code != requests.codes.ok:
        print response.status_code
        print response.text


def find_existing_pack(hardware, lang):
    # Look for an existing language pack. Use X.99.99 as the requesting version as the
    # lp server will only return packs that are for the same major version and a smaller
    # minor version, and we want the most recent pack with the highest minor version there is.
    r = requests.get('{}/v1/languages'.format(lp_url),
                     params={'hardware': hardware,
                             'firmware': '{}.99.99'.format(FIRMWARE_VERSION_TO_UPDATE)},
                     auth=lp_auth)
    assert_ok(r)

    for l in r.json()['languages']:
        if l['ISOLocal'] == lang:
            return l

    return None


def create_new_pack_json(hardware, name, fw_version, metadata):
    return {
             'name': name,
             'localName': metadata['Name'],
             'hardware': hardware,
             'ISOLocal': metadata['Language'],
             'firmware': fw_version,
             'mobile': {
               'name': 'ios',
               'version': '2.6.0'
             }
           }


def pack_json_to_post_data(j):
    # The POST data that the form wants looks like the following...
    #
    # pack[file]:https://language-packs-staging.s3.amazonaws.com/3TRBOAT-en_US.pbl
    # pack[name]:English
    # pack[localName]:English
    # pack[hardware]:bb2
    # pack[ISOLocal]:en_US
    # pack[version]:1
    # pack[firmware]:2.9.0
    # pack[mobile][name]:ios
    # pack[mobile][version]:2.6.0
    #
    # Convert our json into that form

    fields = ('file', 'name', 'localName', 'hardware', 'ISOLocal', 'version', 'firmware')
    d = {'pack[{}]'.format(f): j[f] for f in fields}

    d['pack[mobile][name]'] = j['mobile']['name']
    d['pack[mobile][version]'] = j['mobile']['version']

    return d


def get_lang_metadata(lang_pack):
    mo_filename = None

    # Open up the lang_pack as a pbpack. The first entry is the STRINGS values, which is a mo
    # file. Write this mo file out to a temporary file and use polib to parse out the metadata.
    #
    # The resulting dictionary will look something like the following...
    #
    # {u'Content-Transfer-Encoding': u'8bit',
    #  u'Content-Type': u'text/plain; charset=UTF-8',
    #  u'Language': u'fr_FR',
    #  u'MIME-Version': u'1.0',
    #  u'Name': u'Fran\xe7ais',
    #  u'POT-Creation-Date': u'2016-06-28 15:24-0400',
    #  u'Project-Id-Version': u'33.0',
    #  u'Report-Msgid-Bugs-To': u'',
    #  u'X-Generator': u'POEditor.com'}
    try:
        with open(lang_pack, 'rb') as f:
            pack = pbpack.ResourcePack.deserialize(f, is_system=False)
            with tempfile.NamedTemporaryFile(delete=False) as mo_file:
                mo_filename = mo_file.name

                # RESOURCE_ID_STRINGS is the 0th resource in the pb pack. Grab the data and write
                # it to a file.
                content_index = pack.table_entries[0].content_index
                if len(pack.contents[content_index]) == 0:
                    # Assume this is an english pack
                    return {u'Language': u'en_US',
                            u'Name': u'English',
                            u'Project-Id-Version': u'1.0'}

                mo_file.write(pack.contents[content_index])

        mo_object = polib.mofile(mo_filename)
    finally:
        os.remove(mo_filename)

    metadata = mo_object.metadata

    # Validate the version field just to be sure
    assert 'Project-Id-Version' in metadata
    version_parts = metadata['Project-Id-Version'].split('.')
    assert len(version_parts) == 2
    int(version_parts[0])  # Just make sure it can be converted to a valid integer

    return metadata


def upload_pack_to_s3(lang_pack):
    r = requests.get('{}/admin/packs/sign'.format(lp_url),
                     params={'type': '', 'name': os.path.basename(lang_pack)},
                     auth=lp_auth)
    assert_ok(r)

    s3_upload_info = r.json()

    with open(lang_pack, 'rb') as f:
        r = requests.put(s3_upload_info['signed'], f)
        assert_ok(r)

    return s3_upload_info['url']


def post_edit_to_lp(pack_json):
    post_data = pack_json_to_post_data(pack_json)

    r = requests.post('{}/admin/packs/{}/edit'.format(lp_url, pack_json['id']),
                      data=post_data,
                      auth=lp_auth)
    assert_ok(r)


def create_new_pack_on_lp(pack_json):
    post_data = pack_json_to_post_data(pack_json)

    r = requests.post('{}/admin/packs/add'.format(lp_url),
                      data=post_data,
                      auth=lp_auth)
    assert_ok(r)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('hardware')
    parser.add_argument('lang_pack')
    parser.add_argument('--production', action='store_true')
    parser.add_argument('--allow_new', action='store_true')
    parser.add_argument('--name', default=None)
    parser.add_argument('--fw_version', default='4.0.0')

    args = parser.parse_args()

    global lp_url
    if args.production:
        lp_url = LP_URLS["production"]
    else:
        lp_url = LP_URLS["staging"]

    metadata = get_lang_metadata(args.lang_pack)

    is_new = False

    pack_json = find_existing_pack(args.hardware, metadata['Language'])

    if pack_json is None:
        if not args.allow_new:
            print "No existing pack found"
            return 1

        if args.name is None:
            print "Name is required for new packs"
            return 1

        pack_json = create_new_pack_json(args.hardware, args.name, args.fw_version, metadata)
        is_new = True

    # Update the pack with the new information
    pack_json['version'] = metadata['Project-Id-Version'].split('.')[0]
    pack_json['file'] = upload_pack_to_s3(args.lang_pack)

    if is_new:
        create_new_pack_on_lp(pack_json)
    else:
        post_edit_to_lp(pack_json)


if __name__ == '__main__':
    main()
