# Copyright 2020 The gRPC Authors
#
# 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.
"""Generates grpc-prefixed packages using template renderer.

To use this script, please use 3.7+ interpreter. This script is work-directory
agnostic. A quick executable command:

    python3 tools/distrib/python/grpc_prefixed/generate.py
"""

import dataclasses
import datetime
import logging
import os
import shutil
import subprocess
import sys

import jinja2

WORK_PATH = os.path.realpath(os.path.dirname(__file__))
LICENSE = os.path.join(WORK_PATH, '../../../../LICENSE')
BUILD_PATH = os.path.join(WORK_PATH, 'build')
DIST_PATH = os.path.join(WORK_PATH, 'dist')

env = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.join(WORK_PATH, 'templates')))

LOGGER = logging.getLogger(__name__)
POPEN_TIMEOUT_S = datetime.timedelta(minutes=1).total_seconds()


@dataclasses.dataclass
class PackageMeta:
    """Meta-info of a PyPI package."""
    name: str
    name_long: str
    destination_package: str
    version: str = '1.0.0'


def clean() -> None:
    try:
        shutil.rmtree(BUILD_PATH)
    except FileNotFoundError:
        pass

    try:
        shutil.rmtree(DIST_PATH)
    except FileNotFoundError:
        pass


def generate_package(meta: PackageMeta) -> None:
    # Makes package directory
    package_path = os.path.join(BUILD_PATH, meta.name)
    os.makedirs(package_path, exist_ok=True)

    # Copy license
    shutil.copyfile(LICENSE, os.path.join(package_path, 'LICENSE'))

    # Generates source code
    for template_name in env.list_templates():
        template = env.get_template(template_name)
        with open(
                os.path.join(package_path,
                             template_name.replace('.template', '')), 'w') as f:
            f.write(template.render(dataclasses.asdict(meta)))

    # Creates wheel
    job = subprocess.Popen([
        sys.executable,
        os.path.join(package_path, 'setup.py'), 'sdist', '--dist-dir', DIST_PATH
    ],
                           cwd=package_path,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT)
    outs, _ = job.communicate(timeout=POPEN_TIMEOUT_S)

    # Logs result
    if job.returncode != 0:
        LOGGER.error('Wheel creation failed with %d', job.returncode)
        LOGGER.error(outs)
    else:
        LOGGER.info('Package <%s> generated', meta.name)


def main():
    clean()

    generate_package(
        PackageMeta(name='grpc',
                    name_long='gRPC Python',
                    destination_package='grpcio'))

    generate_package(
        PackageMeta(name='grpc-status',
                    name_long='gRPC Rich Error Status',
                    destination_package='grpcio-status'))

    generate_package(
        PackageMeta(name='grpc-channelz',
                    name_long='gRPC Channel Tracing',
                    destination_package='grpcio-channelz'))

    generate_package(
        PackageMeta(name='grpc-tools',
                    name_long='ProtoBuf Code Generator',
                    destination_package='grpcio-tools'))

    generate_package(
        PackageMeta(name='grpc-reflection',
                    name_long='gRPC Reflection',
                    destination_package='grpcio-reflection'))

    generate_package(
        PackageMeta(name='grpc-testing',
                    name_long='gRPC Testing Utility',
                    destination_package='grpcio-testing'))

    generate_package(
        PackageMeta(name='grpc-health-checking',
                    name_long='gRPC Health Checking',
                    destination_package='grpcio-health-checking'))


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    main()
