#!/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.

# The return code of this script will always be 0, even if there is an error,
# unless the --fail-loudly flag is passed.

import argparse
import tarfile
import json
import os
import shutil
import subprocess
import sys

SRC_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
FUCHSIA_SDK_DIR = os.path.join(SRC_ROOT, 'third_party', 'fuchsia-sdk')
FLUTTER_DIR = os.path.join(SRC_ROOT, 'flutter')


# Prints to stderr.
def eprint(*args, **kwargs):
  print(*args, file=sys.stderr, **kwargs)


def FileNameForSdkPath(sdk_path):
  return sdk_path.split('/')[-1]


def DownloadFuchsiaSDKFromGCS(sdk_path, verbose):
  file = FileNameForSdkPath(sdk_path)
  url = 'https://storage.googleapis.com/fuchsia-artifacts/{}'.format(sdk_path)
  dest = os.path.join(FUCHSIA_SDK_DIR, file)

  if verbose:
    print('Fuchsia SDK url: "%s"' % url)
    print('Fuchsia SDK destination path: "%s"' % dest)

  if os.path.isfile(dest):
    os.unlink(dest)

  # Ensure destination folder exists.
  os.makedirs(FUCHSIA_SDK_DIR, exist_ok=True)
  curl_command = [
      'curl',
      '--retry',
      '3',
      '--continue-at',
      '-',
      '--location',
      '--output',
      dest,
      url,
  ]
  if verbose:
    print('Running: "%s"' % (' '.join(curl_command)))
  curl_result = subprocess.run(
      curl_command,
      stdout=subprocess.PIPE,
      stderr=subprocess.PIPE,
      universal_newlines=True,
  )
  if curl_result.returncode == 0 and verbose:
    print('curl output:stdout:\n{}\nstderr:\n{}'.format(
        curl_result.stdout,
        curl_result.stderr,
    ))
  elif curl_result.returncode != 0:
    eprint(
        'Failed to download: stdout:\n{}\nstderr:\n{}'.format(
            curl_result.stdout,
            curl_result.stderr,
        )
    )
    return None

  return dest


def OnErrorRmTree(func, path, exc_info):
  """
  Error handler for ``shutil.rmtree``.

  If the error is due to an access error (read only file)
  it attempts to add write permission and then retries.
  If the error is for another reason it re-raises the error.

  Usage : ``shutil.rmtree(path, onerror=onerror)``
  """
  import stat
  # Is the error an access error?
  if not os.access(path, os.W_OK):
    os.chmod(path, stat.S_IWUSR)
    func(path)
  else:
    raise


def ExtractGzipArchive(archive, verbose):
  sdk_dest = os.path.join(FUCHSIA_SDK_DIR, 'sdk')
  if os.path.isdir(sdk_dest):
    shutil.rmtree(sdk_dest, onerror=OnErrorRmTree)

  extract_dest = os.path.join(FUCHSIA_SDK_DIR, 'temp')
  if os.path.isdir(extract_dest):
    shutil.rmtree(extract_dest, onerror=OnErrorRmTree)
  os.makedirs(extract_dest, exist_ok=True)

  if verbose:
    print('Extracting "%s" to "%s"' % (archive, extract_dest))

  with tarfile.open(archive, 'r') as z:
    z.extractall(extract_dest)

  shutil.move(extract_dest, sdk_dest)


def Main():
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--fail-loudly',
      action='store_true',
      default=False,
      help="Return an error code if a prebuilt couldn't be fetched and extracted"
  )

  parser.add_argument(
      '--verbose',
      action='store_true',
      default='LUCI_CONTEXT' in os.environ,
      help='Emit verbose output'
  )

  # This flag is unused but is kept to support existing scripts that pass it.
  parser.add_argument('--host-os', help='The host os')

  parser.add_argument('--fuchsia-sdk-path', help='The path in gcs to the fuchsia sdk to download')

  args = parser.parse_args()
  fail_loudly = 1 if args.fail_loudly else 0
  verbose = args.verbose
  fuchsia_sdk_path = args.fuchsia_sdk_path

  if fuchsia_sdk_path is None:
    eprint('sdk_path can not be empty')
    return fail_loudly

  archive = DownloadFuchsiaSDKFromGCS(fuchsia_sdk_path, verbose)
  if archive is None:
    eprint('Failed to download SDK from %s' % fuchsia_sdk_path)
    return fail_loudly

  ExtractGzipArchive(archive, verbose)

  success = True
  return 0 if success else fail_loudly


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