import base64
import json
import logging
import os
import subprocess
import tempfile
import zipfile
from datetime import datetime

import jwt
import requests
from celery import shared_task
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from django.apps import apps
from django.conf import settings
from django.core import files
from django.db.transaction import on_commit

from grandchallenge.algorithms.models import Algorithm
from grandchallenge.codebuild.tasks import create_codebuild_build
from grandchallenge.github.utils import CloneStatusChoices

logger = logging.getLogger(__name__)


def get_repo_url(payload):
    installation_id = payload["installation"]["id"]
    b64_key = settings.GITHUB_PRIVATE_KEY_BASE64
    b64_bytes = b64_key.encode("ascii")
    key_bytes = base64.b64decode(b64_bytes)
    private_key = serialization.load_pem_private_key(
        key_bytes, password=<PASSWORD>, backend=default_backend()
    )
    now = datetime.now()
    msg = {
        "iat": int(now.timestamp()) - 60,
        "exp": int(now.timestamp()) + 60 * 5,
        "iss": settings.GITHUB_APP_ID,
    }
    token = jwt.encode(msg, private_key, algorithm="RS256")
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github.v3+json",
    }
    resp = requests.post(
        f"https://api.github.com/app/installations/{installation_id}/access_tokens",
        headers=headers,
        timeout=10,
    )
    access_token = json.loads(resp.content)["token"]

    repo_url = payload["repository"]["html_url"]
    return repo_url.replace("//", f"//x-access-token:{access_token}@")


def install_lfs():
    process = subprocess.check_output(
        ["git", "lfs", "install"], stderr=subprocess.STDOUT
    )
    return process


def fetch_repo(payload, repo_url, tmpdirname, recurse_submodules):
    cmd = [
        "git",
        "clone",
        "--branch",
        payload["ref"],
        "--depth",
        "1",
        repo_url,
        tmpdirname,
    ]
    if recurse_submodules:
        cmd.insert(2, "--recurse-submodules")

    process = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    return process


def check_license(tmpdirname):
    process = subprocess.Popen(
        ["licensee", "detect", tmpdirname, "--json", "--no-remote"],
        stdout=subprocess.PIPE,
    )
    process.wait()
    return json.loads(process.stdout.read().decode("utf-8"))


def save_zipfile(ghwm, tmpdirname):
    zip_name = f"{ghwm.repo_name}-{ghwm.tag}.zip"
    tmp_zip = tempfile.NamedTemporaryFile()
    with zipfile.ZipFile(tmp_zip.name, "w") as zipf:
        for foldername, _subfolders, filenames in os.walk(tmpdirname):
            for filename in filenames:
                file_path = os.path.join(foldername, filename)
                zipf.write(file_path, file_path.replace(f"{tmpdirname}/", ""))
    temp_file = files.File(tmp_zip, name=zip_name)
    return temp_file


def build_repo(ghwm_pk):
    on_commit(
        lambda: create_codebuild_build.apply_async(kwargs={"pk": ghwm_pk})
    )


@shared_task(**settings.CELERY_TASK_DECORATOR_KWARGS["acks-late-2xlarge"])
def get_zipfile(*, pk):
    GitHubWebhookMessage = apps.get_model(  # noqa: N806
        app_label="github", model_name="GitHubWebhookMessage"
    )
    ghwm = GitHubWebhookMessage.objects.get(pk=pk)

    if ghwm.clone_status == CloneStatusChoices.PENDING:
        payload = ghwm.payload
        repo_url = get_repo_url(payload)
        ghwm.clone_status = CloneStatusChoices.STARTED
        ghwm.save()

        try:
            recurse_submodules = Algorithm.objects.get(
                repo_name=ghwm.payload["repository"]["full_name"]
            ).recurse_submodules
        except Algorithm.DoesNotExist:
            recurse_submodules = False

        with tempfile.TemporaryDirectory() as tmpdirname:
            try:
                # Run git lfs install here, doing it in the dockerfile does not
                # seem to work
                install_lfs()
                fetch_repo(payload, repo_url, tmpdirname, recurse_submodules)
                license_check_result = check_license(tmpdirname)
                temp_file = save_zipfile(ghwm, tmpdirname)

                # update GithubWebhook object
                ghwm.zipfile = temp_file
                ghwm.license_check_result = license_check_result
                ghwm.clone_status = CloneStatusChoices.SUCCESS
                ghwm.save()

                build_repo(ghwm.pk)

            except Exception as e:
                ghwm.stdout = str(getattr(e, "stdout", ""))
                ghwm.stderr = str(getattr(e, "stderr", ""))
                ghwm.clone_status = CloneStatusChoices.FAILURE
                ghwm.save()

                if not ghwm.user_error:
                    raise


@shared_task
def unlink_algorithm(*, pk):
    GitHubWebhookMessage = apps.get_model(  # noqa: N806
        app_label="github", model_name="GitHubWebhookMessage"
    )
    ghwm = GitHubWebhookMessage.objects.get(pk=pk)
    for repo in ghwm.payload["repositories"]:
        Algorithm.objects.filter(repo_name=repo["full_name"]).update(
            repo_name=""
        )

import base64
import json
import logging
import os
import subprocess
import tempfile
import zipfile
from datetime import datetime

import jwt
import requests
from celery import shared_task
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from django.apps import apps
from django.conf import settings
from django.core import files
from django.db.transaction import on_commit

from grandchallenge.algorithms.models import Algorithm
from grandchallenge.codebuild.tasks import create_codebuild_build
from grandchallenge.github.utils import CloneStatusChoices

logger = logging.getLogger(__name__)


def get_repo_url(payload):
    installation_id = payload["installation"]["id"]
    b64_key = settings.GITHUB_PRIVATE_KEY_BASE64
    b64_bytes = b64_key.encode("ascii")
    key_bytes = base64.b64decode(b64_bytes)
    private_key = serialization.load_pem_private_key(
        key_bytes, password=<PASSWORD>, backend=default_backend()
    )
    now = datetime.now()
    msg = {
        "iat": int(now.timestamp()) - 60,
        "exp": int(now.timestamp()) + 60 * 5,
        "iss": settings.GITHUB_APP_ID,
    }
    token = jwt.encode(msg, private_key, algorithm="RS256")
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github.v3+json",
    }
    resp = requests.post(
        f"https://api.github.com/app/installations/{installation_id}/access_tokens",
        headers=headers,
        timeout=10,
    )
    access_token = json.loads(resp.content)["token"]

    repo_url = payload["repository"]["html_url"]
    return repo_url.replace("//", f"//x-access-token:{access_token}@")


def install_lfs():
    process = subprocess.check_output(
        ["git", "lfs", "install"], stderr=subprocess.STDOUT
    )
    return process


def fetch_repo(payload, repo_url, tmpdirname, recurse_submodules):
    cmd = [
        "git",
        "clone",
        "--branch",
        payload["ref"],
        "--depth",
        "1",
        repo_url,
        tmpdirname,
    ]
    if recurse_submodules:
        cmd.insert(2, "--recurse-submodules")

    process = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    return process


def check_license(tmpdirname):
    process = subprocess.Popen(
        ["licensee", "detect", tmpdirname, "--json", "--no-remote"],
        stdout=subprocess.PIPE,
    )
    process.wait()
    return json.loads(process.stdout.read().decode("utf-8"))


def save_zipfile(ghwm, tmpdirname):
    zip_name = f"{ghwm.repo_name}-{ghwm.tag}.zip"
    tmp_zip = tempfile.NamedTemporaryFile()
    with zipfile.ZipFile(tmp_zip.name, "w") as zipf:
        for foldername, _subfolders, filenames in os.walk(tmpdirname):
            for filename in filenames:
                file_path = os.path.join(foldername, filename)
                zipf.write(file_path, file_path.replace(f"{tmpdirname}/", ""))
    temp_file = files.File(tmp_zip, name=zip_name)
    return temp_file


def build_repo(ghwm_pk):
    on_commit(
        lambda: create_codebuild_build.apply_async(kwargs={"pk": ghwm_pk})
    )


@shared_task(**settings.CELERY_TASK_DECORATOR_KWARGS["acks-late-2xlarge"])
def get_zipfile(*, pk):
    GitHubWebhookMessage = apps.get_model(  # noqa: N806
        app_label="github", model_name="GitHubWebhookMessage"
    )
    ghwm = GitHubWebhookMessage.objects.get(pk=pk)

    if ghwm.clone_status == CloneStatusChoices.PENDING:
        payload = ghwm.payload
        repo_url = get_repo_url(payload)
        ghwm.clone_status = CloneStatusChoices.STARTED
        ghwm.save()

        try:
            recurse_submodules = Algorithm.objects.get(
                repo_name=ghwm.payload["repository"]["full_name"]
            ).recurse_submodules
        except Algorithm.DoesNotExist:
            recurse_submodules = False

        with tempfile.TemporaryDirectory() as tmpdirname:
            try:
                # Run git lfs install here, doing it in the dockerfile does not
                # seem to work
                install_lfs()
                fetch_repo(payload, repo_url, tmpdirname, recurse_submodules)
                license_check_result = check_license(tmpdirname)
                temp_file = save_zipfile(ghwm, tmpdirname)

                # update GithubWebhook object
                ghwm.zipfile = temp_file
                ghwm.license_check_result = license_check_result
                ghwm.clone_status = CloneStatusChoices.SUCCESS
                ghwm.save()

                build_repo(ghwm.pk)

            except Exception as e:
                ghwm.stdout = str(getattr(e, "stdout", ""))
                ghwm.stderr = str(getattr(e, "stderr", ""))
                ghwm.clone_status = CloneStatusChoices.FAILURE
                ghwm.save()

                if not ghwm.user_error:
                    raise


@shared_task
def unlink_algorithm(*, pk):
    GitHubWebhookMessage = apps.get_model(  # noqa: N806
        app_label="github", model_name="GitHubWebhookMessage"
    )
    ghwm = GitHubWebhookMessage.objects.get(pk=pk)
    for repo in ghwm.payload["repositories"]:
        Algorithm.objects.filter(repo_name=repo["full_name"]).update(
            repo_name=""
        )

# noqa: N806 # Run git lfs install here, doing it in the dockerfile does not # seem to work # update GithubWebhook object # noqa: N806