package de.hftstuttgart.utils;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.hftstuttgart.config.ModocotProperties;
import de.hftstuttgart.models.JenkinsJobData;
import de.hftstuttgart.rest.v1.jenkins.RestAPIController;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import io.gitea.model.Repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

@Component
public class TaskUploadUtils {

    private static final Logger LOG = LogManager.getLogger(TaskUploadUtils.class);
    private final GitTeaUtil gitTeaUtil;
    private final ModocotProperties modocotProperties;
    private final JGitUtil jGitUtil;
    private final RestCall restCall;

    public TaskUploadUtils(GitTeaUtil gitTeaUtil,
                           ModocotProperties modocotProperties,
                           JGitUtil jGitUtil,
                           RestCall restCall) {
        this.gitTeaUtil = gitTeaUtil;
        this.modocotProperties = modocotProperties;
        this.jGitUtil = jGitUtil;
        this.restCall = restCall;
    }

    /**
     * Start jenkinsJob and return its BuildStatus after completion
     *
     * @param jobId         String jenkinsJobId
     * @param subFolderPath workingDirectory
     * @return Jenkins BuildStatus
     */
    public BuildState startTask(String jobId, String subFolderPath) throws IOException, InterruptedException {
        // we clone the Student submission to /Test subfolder and then copy those files into the working directory
        File f = new File(subFolderPath + "/src/Test");
        for (File fi : f.listFiles()) {
            Files.move(Paths.get(fi.getPath()), Paths.get(subFolderPath + "/src/" + fi.getName()));
        }
        if (f.exists()) f.delete();

        File temp = new File(subFolderPath + "/src/UnitTests/.git");
        if (temp.exists() && temp.isDirectory() && temp.canWrite()) {
            FileUtil.deleteFolderRecursively(temp);
        }
        temp = new File(subFolderPath + "/src/.git");
        if (temp.exists() && temp.isDirectory() && temp.canWrite()) {
            FileUtil.deleteFolderRecursively(temp);
        }

        // creating the jobId.json containing the jobId
        FileWriter fileWriter = new FileWriter(subFolderPath + "/src/jobId.json");
        fileWriter.write(new ObjectMapper().readTree("{\"jobId\":\"" + jobId + "\"}").toString());
        fileWriter.close();

        // creating repository with name jobId
        Repository repository = this.gitTeaUtil.createRepository(jobId);
        repository.setCloneUrl(repository.getCloneUrl().replace("localhost", modocotProperties.getDockerHostIp()));

        // committing work-directory and pushing all files to repository
        this.jGitUtil.commitAllAndPush(new File(subFolderPath), repository, false);

        FileUtil.deleteFolderRecursively(new File(subFolderPath));

        // persisting the jobId
        RestAPIController.JOB_MAP.put(jobId, null);

        createJenkinsJob(jobId, repository.getCloneUrl());
        buildJenkinsJob(jobId);

        // waiting for jenkinsJob to finish then returning its BuildStatus
        int timeout = 0;
        BuildState buildState = getJenkinsBuildState(jobId);
        while (buildState != BuildState.BLUE && buildState != BuildState.RED) {
            buildState = getJenkinsBuildState(jobId);
            if (timeout >= 100) break;
            Thread.sleep(6000);
            timeout++;
        }
        return buildState;
    }

    public void deleteJenkinsJob(String jobId) {
        LOG.info("deleteJenkinsJob jobId: " + jobId);
        ResponseEntity<String> response = this.restCall.exchange("job/" + jobId + "/doDelete", HttpMethod.POST, null, String.class);
    }


    private BuildState getJenkinsBuildState(String jenkinsJob) {
        LOG.info("getJenkinsBuildState jenkinsJob: " + jenkinsJob);
        ResponseEntity<JenkinsJobData> response = this.restCall.exchange("api/json?tree=jobs[name,color]", HttpMethod.GET, null, JenkinsJobData.class);

        if (response.getBody() == null) {
            throw new NullPointerException("Jenkins Response was null");
        }

        return response.getBody()
                .getJobs()
                .stream()
                .filter(job -> job.getName().equals(jenkinsJob))
                .findFirst()
                .get()
                .getColor();

    }

    public String getJenkinsConsoleOutput(String jenkinsJob) {
        ResponseEntity<String> response = this.restCall.exchange("job/" + jenkinsJob + "/lastBuild/consoleText", HttpMethod.GET, null, String.class);
        return response.getBody();
    }

    private void buildJenkinsJob(String user) {
        LOG.info("buildJenkinsJob user: " + user);
        ResponseEntity<String> response = this.restCall.exchange("job/" + user + "/build", HttpMethod.POST, null, String.class);
    }

    private void createJenkinsJob(String gitUser, String gitUrl) {
        LOG.info("createJenkinsJob gitUser: " + gitUser + ", gitUrl: " + gitUrl);
        ResponseEntity<String> response = this.restCall.exchange("createItem?name=" + gitUser, HttpMethod.POST, createXmlFile(gitUser, gitUrl), String.class);
    }

    private String createXmlFile(String gitUser, String gitUrl) {
        // fill data map for template
        Map<String, String> templateData = new HashMap<>();
        templateData.put("gitUser", gitUser);
        templateData.put("gitUrl", gitUrl);

        // freemarker create config
        Configuration cfg = new Configuration(new Version("2.3.30"));
        cfg.setClassForTemplateLoading(this.getClass(), "/templates");
        cfg.setDefaultEncoding("UTF-8");

        // fuse config and data
        StringWriter out = new StringWriter();
        try {
            Template template = cfg.getTemplate("JenkinsFile.ftl");
            template.process(templateData, out);
            LOG.info(String.format("Template created with gitUrl: %s and gitUser: %s", gitUrl, gitUser));
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
        return out.getBuffer().toString();
    }

    public boolean isValidJSON(final String json) {
        boolean valid = false;
        try {
            final JsonParser parser = new ObjectMapper().getFactory().createParser(json);
            while (parser.nextToken() != null) {
            }
            valid = true;
        } catch (IOException e) {
            LOG.error("Json is invalid", e);
        }
        return valid;
    }
}
