import fse from "fs-extra"
import fs from "node:fs"
import path from "node:path"
import { fileURLToPath } from "node:url"
import { spawn } from 'node:child_process'
const { default: packageInfo } = await import(
    "../package.json",
    {
      with: { type: "json" },
    }
  );
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

function loadModule(name) {
    try {
        return require(name);
    } catch (e) {
        return undefined;
    }
}

function copyFile(source, outdir) {
    // 将源文件夹复制到目标
    fse.copy(source, outdir, function (err) {
        if (err) {
            console.log("An error occured while copying the folder.");
            return console.error(err);
        }
    });
}

function switchVersion(version) {
    const source = path.resolve(__dirname, `../lib/v${version}/`);
    try {
        fs.accessSync(source);
    } catch (error) {
        console.log("@qdll/business应用失败");
        return;
    }
    console.log("@qdll/business 正在使用 vue v" + version);
    const outdir = path.resolve(__dirname, `../lib/`);
    copyFile(source, outdir);
}

/**
 * 
 * @param {Object} config spwan config
 * @param {String} config.shell spwan config
 * @param {Function} config.callback spwan config
 * @param {SpawnOptionsWithoutStdio} config.shellConfig spwan config
 * @param {Boolean}  [config.exitListener = true] spwan config
 * @param {(curProcess: ChildProcess) => void} config.setListener spwan config
 * @param {String} config.onEvent spwan config
 * @returns {void}
 */
async function promiseSpawn(config) {
    const {shell, callback,  shellConfig = { shell: true, stdio: 'inherit' }, exitListener = true, setListener} = config
    return new Promise((resolve, reject) => {
        const curProcess = spawn(shell, shellConfig)
        setListener && setListener(curProcess)
        exitListener && curProcess.on('exit', async (...args) => {
            callback && await callback()
            resolve()
        })
        curProcess.on('error', (err) => {
            reject(err)
        })
    })
}
async function getShellOutput(shell) {
    let output = ''
    await new Promise((resolve) => {
        promiseSpawn({
            shell,
            shellConfig: { shell: true },
            setListener: (curProcess) => {
                curProcess.stdout.on('data', data => {
                    output = data.toString().trim()
                    resolve()
                })
            }
        })
    })
    return output
}
async function getGitUserName() {
    return getShellOutput('git config --get user.name')
}
async function getCurBranch() {
    return getShellOutput('git rev-parse --abbrev-ref HEAD')
}

async function addPkgInfoTagAndPushGitOrigin(versionTag) {
    await import('simple-git').then(async ({ simpleGit: git}) => {
        packageInfo.version = versionTag.indexOf('v') === 0 ? versionTag.slice(1) : versionTag
        fs.writeFileSync(path.resolve(__dirname, '../package.json'), JSON.stringify(packageInfo, null, 2))
        await git().add(path.resolve(__dirname, '../package.json'))
        await git().commit(`build: ${versionTag.indexOf('v') === 0 ? versionTag : `v${versionTag}`}`)
        await git().push()
    })
}

async function pushCurTagToOrigin(versionTag, tagMessage) {
    await import('simple-git').then(async ({ simpleGit: git}) => {
        versionTag = versionTag.indexOf('v') === 0 ? versionTag : `v${versionTag}`
        await git().fetch()
        const { all }  = await git().tags()
        if (!all.find(item => item === versionTag)) {
            console.log(versionTag + '远端标签推送中')
            if (tagMessage) {
                await git().addAnnotatedTag(versionTag, tagMessage)
            } else {
                await git().addTag(versionTag)
            }
            await git().pushTags('origin')
        } else {
            console.log(versionTag + '远端标签已存在')
        }
    })
}
/**
 * 
 * @param {string} version1 
 * @param {string} version2 
 * @returns {1|-1|0}
 */
const compareVersion = (version1, version2) => {
    const v1 = version1.split('.');
    const v2 = version2.split('.');
    for (let i = 0; i < v1.length || i < v2.length; ++i) {
        let x = 0, y = 0;
        if (i < v1.length) {
            x = parseInt(v1[i]);
        }
        if (i < v2.length) {
            y = parseInt(v2[i]);
        }
        if (x > y) {
            return 1;
        }
        if (x < y) {
            return -1;
        }
    }
    return 0;
};
/**
 * 
 * @param {string} version1 
 * @param {string} version2 
 * @returns {boolean}
 */
const checkForHugeGapsInVersions = (version1, version2) => {
    const [a, b, c] = version1.split('.').map(Number)
    const [l, m, n] = version2.split('.').map(Number)
    if (a === l && b === m && Math.abs(c - n) > 5) {
        return true
    }
    if (a === l && Math.abs(b - m) > 5) {
        return true
    }
    if (Math.abs(a - l) > 5) {
        return true
    }
    return false
}

/**
 * 
 * @param {string[]} versions 
 */
function findLatestVersion(versions) {
    const versionSortList = versions.map(version => (version.indexOf('v') === 0 ? version.slice(1) : version).split('-')[0])
        .sort(compareVersion).reverse()
    const theNumberOfTimesTheVersionAppeared = versionSortList.reduce((map, item) => {
        if (!map[item]) {
            map[item] = 0
        }
        map[item] += 1
        return map
    }, {})
    let latestVersion = ''
    versionSortList.every((item, index) => {
        if (latestVersion) {
            return false
        }
        const nextVerison = versionSortList[index + 1]
        if (nextVerison && item !== nextVerison  && (theNumberOfTimesTheVersionAppeared[item] > 15 || !checkForHugeGapsInVersions(item, nextVerison))) {
            latestVersion = item
        }
        return true
    })
    return latestVersion
}

export {
    copyFile,
    findLatestVersion,
    promiseSpawn,
    getCurBranch,
    getGitUserName,
    addPkgInfoTagAndPushGitOrigin,
    pushCurTagToOrigin,
    loadModule,
    switchVersion
}
