#!/usr/bin/env python
"""Determine the filename for a JSON model.

This is a utility script that will parse a service's metadata
file and determine where this file should be placed.  It
hides the details of botocore's internal file layout for JSON
models.

It its simplest usage you give it a path to a file and it
prints the location within botocore::

  $ scripts/get-model-filename /tmp/myfile.json
  /Users/foo/botocore/botocore/data/aws/cloudwatch/2010-08-01.normal.json

If you want, you can use the ``-c`` option to actually copy the file
to this location.  When this option is specified, the parent directory
will be created if it does not exist.

  $ scripts/get-model-filename -c /tmp/myfile.json
  /Users/foo/botocore/data/aws/cloudwatch/2010-08-01.normal.json
  Copied: /tmp/myfile.json -> /Users/foo/botocore/data/aws/cloudwatch/2010-08-01.normal.json

"""

import json

# Note we're using optparse for 2.6 compat.
# We don't have particularly complicated command line
# parsing requirements so we'll deal with it.
import optparse
import os
import shutil
import sys
import unittest

try:
    from StringIO import StringIO
except ImportError:
    # Python3 we need to import from the io module.
    from io import StringIO


def determine_json_data_path(fileobj):
    """Determine the filepath for a JSON model.

    This is a utility function that will introspect a JSON file and determine
    where it should be placed in botocore/data/aws/.

    """
    parsed = json.load(fileobj)
    if 'metadata' in parsed:
        return _determine_path_from_metadata(parsed)
    else:
        raise ValueError(
            "Could not determine file path for JSON model, model "
            "is missing required 'metadata' key."
        )


def _determine_path_from_metadata(parsed):
    metadata = parsed['metadata']
    if 'serviceAbbreviation' in metadata:
        service_name = metadata['serviceAbbreviation'].lower().replace(' ', '')
        if service_name.startswith('amazon'):
            service_name = service_name[6:]
        elif service_name.startswith('aws'):
            service_name = service_name[3:]
    else:
        service_name = metadata['endpointPrefix']
        # There's one single special case where we can't automatically
        # figure out, and that's elasticloadbalancing -> elb so we have
        # to hardcode this here.
        if service_name == 'elasticloadbalancing':
            service_name = 'elb'
    api_version = metadata['apiVersion']
    return os.path.join(
        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
        'botocore',
        'data',
        'aws',
        service_name,
        api_version + '.normal.json',
    )


class TestDeterminePath(unittest.TestCase):
    def given_metadata(self, metadata):
        self.metadata = {'metadata': metadata}

    def assert_filename_is(self, filename):
        source_file = StringIO(json.dumps(self.metadata))
        actual = determine_json_data_path(source_file)
        self.assertTrue(actual.endswith(filename))

    def test_can_determine_path_from_metadata(self):
        self.given_metadata(
            {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo'}
        )
        self.assert_filename_is('botocore/data/aws/foo/2015-01-01.normal.json')

        self.given_metadata(
            {
                'apiVersion': '2015-01-01',
                'endpointPrefix': 'foo',
                'serviceAbbreviation': 'Amazon Bar',
            }
        )
        self.assert_filename_is('botocore/data/aws/bar/2015-01-01.normal.json')

        self.given_metadata(
            {
                'apiVersion': '2015-01-01',
                'endpointPrefix': 'foo',
                'serviceAbbreviation': 'AWS Baz',
            }
        )
        self.assert_filename_is('botocore/data/aws/baz/2015-01-01.normal.json')

        self.given_metadata(
            {
                'apiVersion': '2015-01-01',
                'endpointPrefix': 'foo',
                'serviceAbbreviation': 'something else',
            }
        )
        self.assert_filename_is(
            'botocore/data/aws/somethingelse/2015-01-01.normal.json'
        )

        # The special casing of elasticloadbalancing -> elb.
        (
            self.given_metadata(
                {
                    'apiVersion': '2015-01-01',
                    'endpointPrefix': 'elasticloadbalancing',
                }
            ),
        )
        self.assert_filename_is('botocore/data/aws/elb/2015-01-01.normal.json')


def main():
    parser = optparse.OptionParser(usage=__doc__)
    parser.add_option(
        '-c',
        '--copy',
        action='store_true',
        default=False,
        help='Copy the file to the appropriate location. ',
    )
    parser.add_option(
        '-t',
        '--test',
        action='store_true',
        default=False,
        help='Dev use only, run the unit tests.',
    )
    opts, args = parser.parse_args()
    if opts.test:
        sys.argv[:] = [sys.argv[0]]
        unittest.main()
        return 0
    if len(args) != 1:
        sys.stderr.write(
            "Must provide the filename of the JSON model as an argument.\n"
        )
        return 1
    source_filename = args[0]
    if not source_filename.endswith('.normal.json'):
        sys.stderr.write("Only the *.normal.json files are supported.\n")
        return 1
    f = open(source_filename)
    try:
        filename = determine_json_data_path(f)
        sys.stdout.write(filename)
        sys.stdout.write("\n")
    finally:
        f.close()
    if opts.copy:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        shutil.copy(source_filename, filename)
        sys.stdout.write(f"Copied: {source_filename} -> {filename}\n")
    return 0


if __name__ == '__main__':
    sys.exit(main())
