# -*- coding: utf-8 -*-
import os
from os import listdir
from os.path import isfile, join
import stat
import sys
import subprocess
from datetime import datetime
import yaml
from jinja2 import Environment, DebugUndefined, FileSystemLoader
from suite_py.lib.handler.github_handler import GithubHandler
from suite_py.lib.handler import git_handler as git
from suite_py.lib.handler import prompt_utils
from suite_py.lib.logger import Logger
from suite_py.lib.config import Config
from suite_py.lib.handler import drone_handler as drone

github = GithubHandler()
logger = Logger()
config = Config()

now = datetime.now().strftime("%Y%m%d%H%M")

# define paths
projects_root = config.load()["user"]["projects_home"]
artemide_path = os.path.join(projects_root, "artemide")
drone_values_path = f"{artemide_path}/templates/drone/values/"
deploy_scripts_values_path = f"{artemide_path}/templates/deploy_scripts/values/"


def entrypoint():
    # move to artemide root directory
    os.chdir(artemide_path)

    env = Environment(
        loader=FileSystemLoader(artemide_path),
        undefined=DebugUndefined,
        trim_blocks=True,
        lstrip_blocks=True,
    )

    template_type = prompt_utils.ask_choices(
        "Quali file vuoi generare?", ["drone", "deploy_scripts"]
    )
    message = f"Autogenerated update {template_type} from suite-py {now}"

    autobranch = prompt_utils.ask_confirm(
        "Vuoi creare branch e pr in automatico?", default=False
    )

    if autobranch:
        autopush = True
    else:
        autopush = prompt_utils.ask_confirm(
            "Vuoi pushare in automatico?", default=False
        )

    choice = prompt_utils.ask_questions_input(
        "Su quale progetto vuoi lavorare? Inserisci `all` per selezionarli tutti: ",
        "all",
    )

    if choice == "all":
        projects = get_all_templated_projects(template_type)
    else:
        projects = [choice]

    for project in projects:
        logger.info(f"{project}: Controllo che non ci siano file non committati")
        if git.is_dirty(project):
            logger.error(
                f"{project}: Ci sono modifiche non committate, skippo il progetto"
            )
            continue

        project_root_path = os.path.join(projects_root, project)
        branch_name = configure_branch(project, template_type, autobranch, autopush)

        logger.info(f"{project}: Generazione in corso")
        # launch generate
        if template_type == "drone":
            generate_drone(project_root_path, f"{drone_values_path}{project}.yml", env)
        elif template_type == "deploy_scripts":
            generate_deploy_scripts(
                project_root_path, f"{deploy_scripts_values_path}{project}.yml", env
            )
        logger.info(f"{project}: Generazione completata")

        if autobranch or autopush:
            # controllo che sia cambiato effettivamente qualcosa
            if git.is_dirty(project):
                git_operations(project, autobranch, autopush, branch_name, message)
            else:
                logger.warning(
                    f"{project}: nessuna operazione su git da fare. Nessun file modificato"
                )


def get_all_templated_projects(template_type):
    if template_type == "drone":
        values_path = drone_values_path
    elif template_type == "deploy_scripts":
        values_path = deploy_scripts_values_path

    return [
        f.replace(".yml", "")
        for f in listdir(values_path)
        if isfile(join(values_path, f))
    ]


def configure_branch(project, template_type, autobranch, autopush):
    logger.info(f"{project}: Sincronizzo il repo con il remote")
    if autobranch:
        git.sync(project)  # fa anche checkout su master
        return f"t/autogenerated_update_{template_type}_{now}"

    current_branch = git.current_branch_name(project)
    if autopush and git.remote_branch_exists(project, current_branch):
        git.fetch(project)
        git.pull(project)

    return current_branch


def generate_drone(project_path, values_file, env):
    values = yaml.safe_load(open(values_file))
    template = env.get_template(f"templates/drone/{values['template']}.j2")
    rendered = template.render(values)

    write_on_repo(project_path, rendered, ".drone.yml")
    drone.fmt(values["project_name"], project_path)
    drone.validate(values["project_name"], project_path)
    drone.sign(values["project_name"], project_path)


def generate_deploy_scripts(project_path, values_file, env):
    values = yaml.safe_load(open(values_file))
    template = env.get_template(f"templates/deploy_scripts/{values['template']}.j2")

    rendered = template.render(values)
    file_name = "deploy"
    write_on_repo(os.path.join(project_path, "deploy"), rendered, file_name)
    validate_deploy_script(f"{project_path}/{file_name}")

    if values["cloudformation"]:
        template = env.get_template("templates/deploy_scripts/wait_for_stack.j2")
        rendered = template.render(values)
        file_name = "wait_for_stack"
        write_on_repo(os.path.join(project_path, "deploy"), rendered, file_name)
        validate_deploy_script(f"{project_path}/{file_name}")


def write_on_repo(path, rendered, file_name):
    if not os.path.exists(path):
        os.mkdir(path, mode=0o755)

    fd = open(os.path.join(path, file_name), "w")
    fd.write(rendered)
    fd.close()


def fmt(project_path):
    subprocess.run(
        ["drone", "fmt", "--save", ".drone.yml"], cwd=project_path, check=True
    )


def validate_drone(project_path):
    try:
        subprocess.run(
            ["drone", "lint", "--trusted", ".drone.yml"], cwd=project_path, check=True
        )
    except Exception:
        logger.error(f"Template in path {project_path} is not valid")
        sys.exit(-1)


def make_executable(filepath):
    st = os.stat(filepath)
    os.chmod(filepath, st.st_mode | stat.S_IEXEC)


def validate_deploy_script(filepath):
    make_executable(filepath)
    subprocess.run(
        ["shellcheck", filepath], check=True,
    )


def sign(project_path, project):
    subprocess.run(
        ["drone", "sign", f"primait/{project}", "--save"], cwd=project_path, check=True
    )


def git_operations(project, autobranch, autopush, branch_name, message):
    if autobranch:
        logger.info(f"{project}: creo il branch {branch_name}")
        git.checkout(project, branch_name, new=True)
        git.add(project)
        git.commit(project, message)
        git.push(project, branch_name)
        pr = github.create_pr(project, branch_name, message)
        logger.info(f"Pull request numero {pr.number} creata! {pr.html_url}")
    elif autopush:
        if git.current_branch_name(project) != "master":
            git.add(project)
            git.commit(project, message)
            git.push(project, branch_name)
        else:
            logger.warning(
                f"{project} si trova su master, non eseguo il push automatico"
            )
