/*
Explanation of what this script does:

1. It checks if there is already a version tag in the given repo 
 --> if so, it checks if the commit history contains hints wether or not to increase the version number in a certain place
--> if not:
    It will use the default version tag for creating the first

    It then gives back a new version tag and a commit history as outputs to use in later stages

*/

const core = require('@actions/core');
const exec = require('@actions/exec');
const { giteaApi, Api } = require('gitea-js');
const fetch = require('cross-fetch');

/**
 * @type {Api}
 */
let api;

async function Run() {

    try {

        console.log("Running version bumper");

        const inputData = await GetInputData();

        api = giteaApi('http://gitea.int.boger.local', {
            token: inputData.token,
            customFetch: fetch,
        });

        const lastTag = await TryGetLastTag(inputData);

        console.log("Last found tag to work with: ", lastTag);

        const commitHistory = await GetCommitHistory(inputData, lastTag);

        console.log("Commit history: ", commitHistory);

        const newTagData = await CreateNewTagData(inputData, lastTag, commitHistory); 

        console.log("New tag data: ", newTagData);

        const patchNotes = await BuildPatchNotes(inputData, commitHistory);

        console.log("Patchnotes: ", patchNotes);

        // Output the new version
        core.setOutput('newTag', newTagData.newTag);

        if(patchNotes != null) core.setOutput('patchNotes', patchNotes);

    } catch (error) {

        console.log(error.message);
        core.setFailed(error.message);
    
    }

}

async function GetInputData() {

    // Input parameters
    const repo_name_comb_name = core.getInput('gitea_repo').split("/");

    const res = {
        token: core.getInput('gitea_token'),
        owner: repo_name_comb_name[0],
        repo: repo_name_comb_name[1],
        actor: core.getInput('gitea_actor'),
        majorKeywords: core.getInput('majorKeywords').split(',').map(x => x.trim()),
        minorKeywords: core.getInput('minorKeywords').split(',').map(x => x.trim())
    };

    return res;

}

async function GetCommitHistory(inputData, lastTag) {
    
    if(lastTag == null || lastTag == '') return null;

    const owner = inputData.owner;
    const repo_name = inputData.repo;

    // Get the commit history from the tag to the latest commit
    const commitsResponse = await api.repos.repoCompareDiff(owner, repo_name, `${lastTag}...main`);
    
    const commitHistory = commitsResponse.data.commits.map(commit => commit.commit.message);
    const commitHistoryHeads = commitHistory.map(x => x.split('\n')[0].trim());
    const commitHistoryHeadsDistinct = [...new Set(commitHistoryHeads)];

    return {
        commitHistory: commitHistory,
        commitHistoryHeadsDistinct: commitHistoryHeadsDistinct
    };

}

/**
 * 
 * @returns {Promise<string?>}
 */
async function TryGetLastTag(inputData) {
    
    try {

        const owner = inputData.owner;
        const repo_name = inputData.repo;

        const response = await api.repos.repoListTags(owner, repo_name);

        const tags = response.data.map(tag => tag.name);

        if(tags.length > 0) {
            return tags[0];
        }

    } catch(error) {

        console.log(error.message);

    }

    return null;

}

async function CreateNewTagData(inputData, lastTag, commitHistory) {
    
    //no first tag existing
    if(lastTag == null || lastTag == '') {
        var defo = core.getInput('default_base_version_tag');
        return {
            newTag: defo,
            lastTag: lastTag,
        };
    }

    // Check if commits contain any major or minor keywords
    let newVersion = [];
    const regex = /.*(\d+)\.(\d+)\.(\d+).*/;
    const matches = regex.exec(lastTag);
    
    if (matches) {
        const [, majorVersion, minorVersion, bugfixVersion] = matches;
        newVersion = [majorVersion, minorVersion, bugfixVersion];
        console.log("Tag match found", newVersion);
    } else {
        console.error("No tag match found.");
    }

    let majorChanged = false, minorChanged = false;

    // check for minor or major changes if a commit history is avail
    if(commitHistory != null) {

        console.log("Checking commit history for keywords..");

        for (const commit of commitHistory.commitHistory) {

            for (const keyword of inputData.majorKeywords) {
            
                if (commit.toLowerCase().includes(keyword.toLowerCase())) {
                    newVersion[0] = (parseInt(newVersion[0]) + 1).toString();
                    newVersion[1] = '0';
                    newVersion[2] = '0';
                    majorChanged = true;
                    console.log("Found major keyword in: ", commit);
                    break;
                }
            
            }

            if(majorChanged || minorChanged) break;

            for (const keyword of inputData.minorKeywords) {
            
                if (commit.toLowerCase().includes(keyword.toLowerCase())) {
                    newVersion[1] = (parseInt(newVersion[1]) + 1).toString();
                    newVersion[2] = '0';
                    minorChanged = true;
                    console.log("Found minor keyword in: ", commit);
                    break;
                }
            }

            if(majorChanged || minorChanged) break;

        }
    }

    //no major minor changed, increase bugfix
    if(!majorChanged && !minorChanged) {

        newVersion[2] = (parseInt(newVersion[2]) + 1).toString();

    }

    return {
        newTag: newVersion.join('.'),
        lastTag: lastTag,
    };

}

async function BuildPatchNotes(inputData, commitHistory) {
 
    if(commitHistory == null) return null;

    // Build simple patchnotes

    const commitListString = commitHistory.commitHistoryHeadsDistinct.join('\n- ');

    let body = `Released by: @${inputData.actor}`;

    if(commitHistory.commitHistoryHeadsDistinct.length > 0) {
        body += `\nCommits: ${commitHistory.commitHistoryHeadsDistinct.length}\n\n- ${commitListString}`;
    }

    return body;

}

async function CreateReleaseAndTag(forTag) {

}

// Run the action
Run();