#!/usr/bin/env python3
# Copyright 2013 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Reads the contents of a package config file generated by the build and
  converts it to a real package_config.json file
"""

import argparse
import collections
import json
import os
import re
import sys

THIS_DIR = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(THIS_DIR, '..', '..', '..', 'third_party', 'pyyaml', 'lib'))
import yaml

DEFAULT_LANGUAGE_VERSION = '2.8'

Package = collections.namedtuple('Package', ['name', 'rootUri', 'languageVersion', 'packageUri'])


class PackageConfig:
  # The version of the package config.
  VERSION = 2

  # The name of the generator which gets written to the json output
  GENERATOR_NAME = os.path.basename(__file__)

  def __init__(self, packages):
    self.packages = packages

  def asdict(self):
    """Converts the package config to a dictionary"""
    return {
        'configVersion': self.VERSION,
        'packages': [p._asdict() for p in sorted(self.packages)],
        'generator': self.GENERATOR_NAME,
    }


def language_version_from_pubspec(pubspec):
  """Parse the content of a pubspec.yaml"""
  with open(pubspec) as pubspec:
    parsed = yaml.safe_load(pubspec)
    if not parsed:
      return DEFAULT_LANGUAGE_VERSION

    # If any format like:
    #   sdk: '>=a.b'
    #   sdk: '^a.b'
    #   sdk: 'a.b'
    # ... is found, we 'a.b' as the language version.
    #
    # In all other cases we default to "2.8"
    env_sdk = parsed.get('environment', {}).get('sdk', 'any')
    match = re.search(r'^(>=|\^)?((0|[1-9]\d*)\.(0|[1-9]\d*))', env_sdk)
    if match:
      min_sdk_version = match.group(2)
    else:
      min_sdk_version = DEFAULT_LANGUAGE_VERSION

    return min_sdk_version


def collect_packages(items, relative_to):
  """Reads metadata produced by GN to create lists of packages and pubspecs.
        - items: a list of objects collected from gn
        - relative_to: The directory which the packages are relative to. This is
          the location that contains the package_config.json file

    Returns None if there was a problem parsing packages
    """
  packages = []
  pubspec_paths = []
  for item in items:
    if 'language_version' in item:
      language_version = item['language_version']
    elif 'pubspec_path' in item:
      pubspec_paths.append(item['pubspec_path'])
      language_version = language_version_from_pubspec(item['pubspec_path'])
    else:
      language_version = DEFAULT_LANGUAGE_VERSION

    package = Package(
        name=item['name'],
        rootUri=os.path.relpath(item['root_uri'], relative_to),
        languageVersion=language_version,
        packageUri=item['package_uri']
    )

    # TODO(fxbug.dev/56428): enable once we sort out our duplicate packages
    # for p in packages:
    #   if p.rootUri == package.rootUri:
    #     print('Failed to create package_config.json file')
    #     print('The following packages contain the same package root ' + p.rootUri)
    #     print('  - ' + p.rootUri)
    #     print('  - ' + package.rootUri)
    #     return None

    packages.append(package)

  return packages, pubspec_paths


def main():
  parser = argparse.ArgumentParser(description=__doc__)
  parser.add_argument('--input', help='Path to original package_config', required=True)
  parser.add_argument('--output', help='Path to the updated package_config', required=True)
  parser.add_argument('--root', help='Path to fuchsia root', required=True)
  parser.add_argument('--depfile', help='Path to the depfile', required=True)
  args = parser.parse_args()

  with open(args.input, 'r') as input_file:
    contents = json.load(input_file)

  output_dir = os.path.dirname(os.path.abspath(args.output))
  packages, pubspec_paths = collect_packages(contents, output_dir)
  if packages is None:
    return 1

  with open(args.depfile, 'w') as depfile:
    depfile.write('%s: %s' % (args.output, ' '.join(pubspec_paths)))

  with open(args.output, 'w') as output_file:
    package_config = PackageConfig(packages)
    json.dump(
        package_config.asdict(), output_file, indent=2, sort_keys=True, separators=(',', ': ')
    )

  return 0


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