import {dest, series, src} from 'gulp';
import process from 'node:process';
import replace from 'gulp-replace';
import {getRandomValues} from 'node:crypto';
import * as dartSass from 'sass';
import gulpSass from 'gulp-sass';
import {deleteAsync} from 'del';
import {readdirSync, readFileSync, writeFileSync, rmdirSync, existsSync, mkdirSync} from "fs";
import {join} from 'node:path';

import {compilePack} from '@foundryvtt/foundryvtt-cli';

const sass = gulpSass(dartSass);


/**
 * Generate a random alphanumeric string ID of a given requested length using `crypto.getRandomValues()`.
 * @param {number} length    The length of the random string to generate, which must be at most 16384.
 * @returns {string}         A string containing random letters (A-Z, a-z) and numbers (0-9).
 */
function randomID(length = 16) {
    const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    const cutoff = 0x100000000 - (0x100000000 % chars.length);
    const random = new Uint32Array(length);
    do {
        getRandomValues(random);
    } while (random.some(x => x >= cutoff));
    let id = "";
    for (let i = 0; i < length; i++) id += chars[random[i] % chars.length];
    return id;
}

const convert = function (from, to, ofType) {

    const SOURCE = from;
    const DEST = to;
    const TYPE = ofType;

    try {
        rmdirSync(DEST, {force: true, recursive: true})
    } catch (e) {
    }
    mkdirSync(DEST)

    readdirSync(SOURCE).forEach(file => {
        let originalSource = JSON.parse(readFileSync(join(SOURCE, file), {encoding: "utf8"}));
        let id = randomID();

        let targetSource = {
            _id: id,
            _key: "!items!" + id,
            type: TYPE,
            img: originalSource.image,
            name: originalSource.name.trim(),
            system: {...originalSource},
        }
        delete targetSource.system.image;
        let target = JSON.stringify(targetSource, null, 2);
        let newFileName = "./" + join(DEST, id + ".json");
        writeFileSync(newFileName, target, {encoding: "utf8"});
    });

}

function cleanDist() {
    return deleteAsync(['dist/**']);
}

function buildStyles() {
    return src('src/style/**/*.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(dest('dist/style/'))
}

function copySource() {
    return src(['src/**/*', '!src/assets/**/*', '!src/style/**/*.scss', '!src/packs/**/*'])
        .pipe(dest('dist/'));
}

function copyAssets() {
    return src(['src/assets/**/*'], {encoding: false})
        .pipe(dest('dist/assets/'))
}

function updateManifestFile() {
    return src('dist/system.json')
        .pipe(replace('{{VERSION}}', process.env.VERSION))
        .pipe(dest('dist/'))
}

async function prepareDB() {

    try {
        if (!existsSync("./src/packs/__source")) {
            mkdirSync("./src/packs/__source");
        }

        convert("./src/packs/_source/talente", "./src/packs/__source/talente", "Skill");
        convert("./src/packs/_source/zauber", "./src/packs/__source/zauber", "Spell");
        convert("./src/packs/_source/vorteile", "./src/packs/__source/vorteile", "Advantage");
        convert("./src/packs/_source/waffen", "./src/packs/__source/waffen", "Equipment");
        convert("./src/packs/_source/munition", "./src/packs/__source/munition", "Equipment");
        convert("./src/packs/_source/ruestzeug", "./src/packs/__source/ruestzeug", "Equipment");
        convert("./src/packs/_source/liturgien-und-segnungen", "./src/packs/__source/liturgien", "Liturgy");
        convert("./src/packs/_source/wunden", "./src/packs/__source/wunden", "ActiveEffect");

    } catch (err) {
        console.error(err);
    }

}

function buildDB() {
    // Determine which source folders to process

    const PACK_SRC = "src/packs/__source"
    const PACK_DEST = "dist/packs/"

    return new Promise(async (resolve, reject) => {
        const folders = readdirSync(PACK_SRC, {withFileTypes: true}).filter(file =>
            file.isDirectory()
        );

        for (const folder of folders) {
            const src = join(PACK_SRC, folder.name);
            const dest = join(PACK_DEST, folder.name);
            console.info(`Compiling pack ${folder.name}`);
            await compilePack(src, dest, {recursive: true, nedb: false});

        }
        resolve()
    })
}


export default series(
    cleanDist,
    copySource,
    copyAssets,
    buildStyles,
    prepareDB,
    buildDB,
    updateManifestFile
)
