# -*- coding: utf-8 -*-
import os
from os import listdir
from os.path import isfile, join
import stat
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}/deploy/{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}/deploy/{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 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 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"
            )
