import requests
import base64
import re
import os

OPERATION_ADD = "ADD"
OPERATION_UPDATE = "UPDATE"


def generate_template_metadata(ff):
    versions = re.findall(r'(?:(\d+\.(?:\d+\.)*\d+))', ff.get("file_name"))
    return "+++\n" \
           "title = \"{0}\"\n" \
           "lastModified = \"{1}\"\n" \
           "imageUrl = \"{2}\"\n" \
           "remark = \"THIS METADATA IS AUTOGENERATED FOR TRACKING, " \
           "PLEASE DON'T UPDATE IT MANUALLY.\"\n" \
           "versions = [\"{3}\"]\n" \
           "+++".format(os.path.splitext(ff.get("file_name"))[0],
                        ff.get("lastModified"),
                        ff.get("image_file"),
                        "" if len(versions) == 0 else versions[0])


class GiteeHandler:

    def __init__(self, access_token, owner, project, branch, committer_name,
                 committer_email):
        self.access_token = access_token
        self.owner = owner
        self.project = project
        self.branch = branch
        self.committer_name = committer_name
        self.committer_email = committer_email
        self.base_url = \
            "https://gitee.com/api/v5/repos/{0}/{1}/contents".format(
                self.owner, self.project)
        self.blob_url = "https://gitee.com/api/v5/repos/{0}/{1}/git/blobs".format(
            self.owner, self.project)

    def prepare_release_file_content(self, latest_file):
        with open(latest_file.get("local_path"), 'r+') as f:
            content = f.read()
            return generate_template_metadata(
                latest_file).rstrip('\r\n') + '\n' + content

    def file_need_update(self, existing_file, latest_file):
        # Get raw content from API
        response = requests.get("{0}/{1}?access_token={2}".format(
            self.blob_url, existing_file.get("sha"), self.access_token))
        if response.status_code != requests.codes.ok:
            raise Exception(
                "failed to get release file content {0} from gitee, "
                "error {0}".format(existing_file.get("name"), response.text))
        result = re.search('lastModified = \"(.*)\"',
                           base64.standard_b64decode(
                               response.json().get("content"))).group(1)
        if result is not None:
            if str(result) == latest_file.get("lastModified"):
                return False
        print(
            "file {0}'s last modified time changed from {1} "
            "to {2} will update this file.".format(
                latest_file.get("file_name"),
                result, latest_file.get("lastModified")))
        return True

    def get_release_file_operations(self, folder_path, files):
        operation_files = []
        # 1. Collect all existing files on gitee
        response = requests.get("{0}/{1}?access_token={2}&ref={3}".format(
            self.base_url, folder_path, self.access_token, self.branch))
        if response.status_code != requests.codes.ok:
            raise Exception("failed to get exsting release file from gitee, "
                            "error {0}".format(response.text))
        # determine whether file needs update or add
        for r_file in files:
            existing_file = filter(lambda ff: ff.get("name") == r_file.get(
                "file_name"), response.json())
            if len(existing_file) == 0:
                r_file["operation"] = OPERATION_ADD
                operation_files.append(r_file)
                continue
            if self.file_need_update(existing_file[0], r_file):
                r_file["operation"] = OPERATION_UPDATE
                r_file["sha"] = existing_file[0].get("sha")
                operation_files.append(r_file)
        return operation_files

    def refresh_release_files(self, folder_path, files):
        operation_files = self.get_release_file_operations(folder_path, files)
        if len(operation_files) == 0:
            print("no changes are detected, skipping refreshing files.")
        for f in operation_files:
            content = self.prepare_release_file_content(f)
            self.add_or_update_release_file(f, folder_path, content)

    def add_or_update_release_file(self, latest_file, folder_path, content):
        print("starting to update/create file {0} onto gitee.".format(
            latest_file.get("file_name")))
        method = requests.post
        data = {
            "access_token": self.access_token,
            "content": base64.standard_b64encode(content),
            "message": "file {0} updated via functiongraph "
                       "triggered by OBS Update".format(
                latest_file.get("file_name")),
            "branch": self.branch,
            "committer[name]": self.committer_name,
            "committer[email]": self.committer_email,
            "author[name]": self.committer_name,
            "author[email]": self.committer_email,
        }
        if latest_file.get("operation") == OPERATION_UPDATE:
            data["sha"] = latest_file.get("sha")
            method = requests.put

        response = method("{0}/{1}/{2}".format(
            self.base_url, folder_path, latest_file.get("file_name")),
            data=data)
        print("{0}/{1}".format(self.base_url, latest_file.get("key")))
        if response.status_code not in [200, 201, 204]:
            raise Exception(
                "failed to update release file {0} "
                "onto gitee, error {1}".format(
                    latest_file.get("key"), response.text))
