import requests
from zipfile import ZipFile, ZipInfo
import os
import pathlib

from proto_builder.core.file_utils import delete_folder, delete_file
from proto_builder.core.check import check_go, check_node
from proto_builder.core.bin_manager import get_platform, ProtoBin
import proto_builder

import click

ROOT_FOLDER = pathlib.Path(proto_builder.__file__).parent

# Folder where the protoc binary will be stored after download.
PROTOC_ROOT_BIN_FOLDER = ROOT_FOLDER.joinpath("protobin")

# Folder where the build result from Kotlin will be, variable used for clean up only.
BUILD_FOLDER = ROOT_FOLDER.joinpath("build")


class ZipFileWithPermissions(ZipFile):
    """Custom ZipFile class handling file permissions."""

    def _extract_member(self, member, targetpath, pwd):
        if not isinstance(member, ZipInfo):
            member = self.getinfo(member)

        targetpath = super()._extract_member(member, targetpath, pwd)

        attr = member.external_attr >> 16
        if attr != 0:
            os.chmod(targetpath, attr)
        return targetpath


def download_file(url: str):
    """
    Download file from an url.
    :param url Address from the file to be downloaded.
    """
    r = requests.get(url)
    filename = url.split("/")[-1]
    open(filename, "wb").write(r.content)


def setup():
    """
    Initialize all necessary files.

    This will download and install all necessary files to build the libraries
    for Go and Kotlin.
    """
    system = get_platform()
    download_file(system.proto_c_url)

    with ZipFileWithPermissions(system.proto_c_zip_file_name, "r") as zip_ref:
        zip_ref.extractall(PROTOC_ROOT_BIN_FOLDER)

    delete_file(system.proto_c_zip_file_name)
    click.secho("Protoc binary downloaded and extracted", fg='green')

    setup_go()
    setup_node()
    click.secho(f"Finish setup. Binaries are on folder {PROTOC_ROOT_BIN_FOLDER}", fg='green')


def setup_go():
    """Check if all dependencies for Go are installed on the system. And install the necessary libraries."""
    if not check_go():
        raise SystemError("Install Go before proceeding.")

    os.system("""
    go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
    """)
    click.secho("Finish installing go plugin for protoc", fg='green')


def setup_node():
    """
    Check if all dependencies for Typescript are installed on the system. And install
    the necessary libraries.
    """
    if not check_node():
        raise SystemError("Install Node before proceeding.")

    click.secho("Installing proto-gen-ts to generate typescript files.")
    os.chdir(f"{PROTOC_ROOT_BIN_FOLDER}/bin")
    os.system("npm install protoc-gen-ts")

    click.secho("Protoc-web plugin for TS downloaded and extracted", fg='green')


def clean_setup():
    """Clean everything that was created by the setup execution."""
    click.secho("Removing all folders created when executing setup command.", fg='red')
    delete_folder(PROTOC_ROOT_BIN_FOLDER, BUILD_FOLDER)
