# Copyright (C) 2018 The Android Open Source Project
#
# 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.
"""Module to check updates from Git upstream."""


import datetime

import fileutils
import git_utils
import metadata_pb2    # pylint: disable=import-error
import updater_utils


class GitUpdater():
    """Updater for Git upstream."""

    def __init__(self, url, proj_path, metadata):
        if url.type != metadata_pb2.URL.GIT:
            raise ValueError('Only support GIT upstream.')
        self.proj_path = proj_path
        self.metadata = metadata
        self.upstream_url = url
        self.upstream_remote_name = None
        self.android_remote_name = None
        self.new_version = None
        self.merge_from = None

    def _setup_remote(self):
        remotes = git_utils.list_remotes(self.proj_path)
        for name, url in remotes.items():
            if url == self.upstream_url.value:
                self.upstream_remote_name = name

            # Guess android remote name.
            if '/platform/external/' in url:
                self.android_remote_name = name

        if self.upstream_remote_name is None:
            self.upstream_remote_name = "update_origin"
            git_utils.add_remote(self.proj_path, self.upstream_remote_name,
                                 self.upstream_url.value)

        git_utils.fetch(self.proj_path,
                        [self.upstream_remote_name, self.android_remote_name])

    def check(self):
        """Checks upstream and returns whether a new version is available."""

        self._setup_remote()
        if git_utils.is_commit(self.metadata.third_party.version):
            # Update to remote head.
            return self._check_head()

        # Update to latest version tag.
        return self._check_tag()

    def _check_tag(self):
        tags = git_utils.list_remote_tags(self.proj_path,
                                          self.upstream_remote_name)
        current_ver = self.metadata.third_party.version
        self.new_version = updater_utils.get_latest_version(
            current_ver, tags)
        self.merge_from = self.new_version
        print('Current version: {}. Latest version: {}'.format(
            current_ver, self.new_version), end='')
        return self.new_version != current_ver

    def _check_head(self):
        commits = git_utils.get_commits_ahead(
            self.proj_path, self.upstream_remote_name + '/master',
            self.android_remote_name + '/master')

        if not commits:
            return False

        self.new_version = commits[0]

        # See whether we have a local upstream.
        branches = git_utils.list_remote_branches(
            self.proj_path, self.android_remote_name)
        upstreams = [
            branch for branch in branches if branch.startswith('upstream-')]
        if upstreams:
            self.merge_from = '{}/{}'.format(
                self.android_remote_name, upstreams[0])
        else:
            self.merge_from = 'update_origin/master'

        commit_time = git_utils.get_commit_time(self.proj_path, commits[-1])
        time_behind = datetime.datetime.now() - commit_time
        print('{} commits ({} days) behind.'.format(
            len(commits), time_behind.days), end='')
        return True

    def _write_metadata(self, path):
        updated_metadata = metadata_pb2.MetaData()
        updated_metadata.CopyFrom(self.metadata)
        updated_metadata.third_party.version = self.new_version
        fileutils.write_metadata(path, updated_metadata)

    def update(self):
        """Updates the package.

        Has to call check() before this function.
        """
        upstream_branch = self.upstream_remote_name + '/master'

        commits = git_utils.get_commits_ahead(
            self.proj_path, self.merge_from, upstream_branch)
        if commits:
            print('{} is {} commits ahead of {}. {}'.format(
                self.merge_from, len(commits), upstream_branch, commits))

        commits = git_utils.get_commits_ahead(
            self.proj_path, upstream_branch, self.merge_from)
        if commits:
            print('{} is {} commits behind of {}.'.format(
                self.merge_from, len(commits), upstream_branch))

        self._write_metadata(self.proj_path)
        print("""
This tool only updates METADATA. Run the following command to update:
    git merge {merge_branch}

To check all local changes:
    git diff {merge_branch} HEAD
""".format(merge_branch=self.merge_from))