import {dest, series, src} from 'gulp';
import gulp from 'gulp';
import process from 'node:process';
import jsonModify from 'gulp-json-modify';
import {subtle} from 'node:crypto';
import * as dartSass from 'sass';
import gulpSass from 'gulp-sass';
import {deleteAsync} from 'del';
import {readdirSync, readFileSync, writeFileSync, rmdirSync, existsSync, mkdirSync, statSync} 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 {string} reference The reference which should be used to generate a semi random ID
 * @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(reference = "", length = 16) {
    const encoder = new TextEncoder()
    const data = encoder.encode(reference)
    return subtle.digest('SHA-256', data).then(hashBuffer => {
        // Step 2: Convert the hash to a Base62 string
        const base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
        const hashArray = new Uint8Array(hashBuffer)
        let num = BigInt(0)

        // Convert hash buffer to a BigInt
        for (let byte of hashArray) {
            num = (num << BigInt(8)) | BigInt(byte)
        }

        let base62Id = '';
        while (num > 0) {
            const remainder = num % BigInt(62)
            base62Id = base62Chars[Number(remainder)] + base62Id
            num = num / BigInt(62)
        }

        // Step 3: Return the first 16 characters
        return base62Id.slice(-length)
    });
}

const convert = function (from, to, ofType, overwrite = true) {
    const SOURCE = from;
    const DEST = to;
    const TYPE = ofType;

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

    let promises = []

    const filewalker = async (source) => {
        console.debug("entering directory", source)
        for (let file of readdirSync(source)) {
            if (statSync(join(source, file)).isDirectory()) {
                await filewalker(join(source, file))
            } else {
                if (file.endsWith(".json")) {
                    console.debug("processing file", join(source, file))
                    let originalSource = JSON.parse(readFileSync(join(source, file), {encoding: "utf8"}))
                    promises.push(new Promise((resolve2) => {
                        randomID("DSA_4-1" + TYPE + originalSource.name.trim()).then(id => {
                            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")
                            if (!existsSync(join("./", DEST))) {
                                mkdirSync(join("./", DEST))
                            }
                            writeFileSync(newFileName, target, {encoding: "utf8"})
                            resolve2()
                        })

                    }))
                }
            }
        }
    }

    filewalker(SOURCE)

    return Promise.allSettled(promises)
}

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('src/system.json')
        .pipe(
            jsonModify({
                key: "version",
                value: process.env.VERSION
            })
        )
        .pipe(
            jsonModify({
                key: "download",
                value: "https://git.macniel.online/macniel/foundry-dsa41-game/releases/download/{{VERSION}}/release.zip".replace("{{VERSION}}", process.env.VERSION)
            })
        )
        .pipe(dest('src/'))
}

gulp.task('prepareDB', async function (done) {

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

        await convert("./src/packs/_source/talente", "./src/packs/__source/talente", "Skill")
        await convert("./src/packs/_source/zauber", "./src/packs/__source/zauber", "Spell")
        await convert("./src/packs/_source/vorteile", "./src/packs/__source/vorteile", "Advantage")
        await convert("./src/packs/_source/nachteile", "./src/packs/__source/vorteile", "Advantage", false)
        await convert("./src/packs/_source/sonderfertigkeiten", "./src/packs/__source/sonderfertigkeiten", "SpecialAbility")
        await convert("./src/packs/_source/waehrungen", "./src/packs/__source/waehrungen", "Equipment")

        await convert("./src/packs/_source/Gegenstaende/Behaelter", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Beleuchtung", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Buecher", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Essutensilien", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Fahrzeuge", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Feinmechanik", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Freizeitbedarf", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Gewuerze", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Gifte", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Heilkraeuter", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Kleidung", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Koerperpflege", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Munition", "./src/packs/__source/munition", "Equipment")
        await convert("./src/packs/_source/Gegenstaende/Musikinstrumente", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Reisebedarf", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Ruestzeug", "./src/packs/__source/ruestzeug", "Equipment")
        await convert("./src/packs/_source/Gegenstaende/Schmuck", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Schreibbedarf", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Seile", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Tierbedarf", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Waffen", "./src/packs/__source/waffen", "Equipment")
        await convert("./src/packs/_source/Gegenstaende/Waffenzubehoer", "./src/packs/__source/gegenstaende", "Equipment", false)
        await convert("./src/packs/_source/Gegenstaende/Werkzeug", "./src/packs/__source/gegenstaende", "Equipment", false)

        await convert("./src/packs/_source/liturgien-und-segnungen", "./src/packs/__source/liturgien", "Liturgy")
        await convert("./src/packs/_source/wunden", "./src/packs/__source/wunden", "ActiveEffect")

        await convert("./src/packs/_source/kulturen", "./src/packs/__source/kulturen", "Culture")
        await convert("./src/packs/_source/spezien", "./src/packs/__source/spezien", "Species")
        await convert("./src/packs/_source/professionen", "./src/packs/__source/professionen", "Profession")
        done()
    } catch (err) {
        console.error(err)
    }
})


gulp.task('buildDB', function (done) {
    // Determine which source folders to process

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

    return new Promise(async (resolve) => {
        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()
        done()
    })
})


export default series(
    cleanDist,
    updateManifestFile,
    copySource,
    copyAssets,
    buildStyles,
    gulp.task('prepareDB'),
    gulp.task('buildDB')
)
