import fs, { existsSync, statSync } from 'fs';
import crypto from 'crypto';
import { relative } from 'path';
import { needleLog } from '../vite/logging.js';


/** Create a file containing information about the build inside the build directory
 * @param {String} buildDirectory 
 * @param {{log?:boolean}} [options]
 */
export function createBuildInfoFile(buildDirectory, options = undefined) {
    const shouldLog = options?.log !== false;
    const rel = (pathValue) => {
        const value = relative(process.cwd(), pathValue).replaceAll("\\", "/");
        return value?.length ? value : ".";
    };

    if (!buildDirectory) {
        if (shouldLog) needleLog("needle-buildinfo", "Can not create build info file because \"buildDirectory\" is not defined", "warn", { dimBody: false });
        return { ok: false, error: "buildDirectory is not defined" };
    }
    // start creating the buildinfo
    const buildInfo = {
        time: new Date().toISOString(),
        totalsize: 0,
        files: []
    };
    if (shouldLog) needleLog("needle-buildinfo", `Begin collecting files in \"${rel(buildDirectory)}\"`);
    recursivelyCollectFiles(buildDirectory, "", buildInfo);
    const buildInfoPath = `${buildDirectory}/needle.buildinfo.json`;
    const totalSizeInMB = buildInfo.totalsize / 1024 / 1024;
    if (shouldLog) needleLog("needle-buildinfo", `Collected ${buildInfo.files.length} files (${totalSizeInMB.toFixed(2)} MB). Writing build info to \"${rel(buildInfoPath)}\"`);
    const str = JSON.stringify(buildInfo);
    fs.writeFileSync(buildInfoPath, str);
    if(!existsSync(buildDirectory)) {
        if (shouldLog) needleLog("needle-buildinfo", `Could not write build info file to \"${rel(buildInfoPath)}\"`, "warn", { dimBody: false });
        return { ok: false, error: `Could not write build info file to \"${rel(buildInfoPath)}\"` };
    }
    if (shouldLog) needleLog("needle-buildinfo", `Build info file successfully written to \"${rel(buildInfoPath)}\"`);
    return {
        ok: true,
        buildDirectory: rel(buildDirectory),
        buildInfoPath: rel(buildInfoPath),
        fileCount: buildInfo.files.length,
        totalSizeBytes: buildInfo.totalsize,
        totalSizeInMB,
    };
}

export function collectBuildDirectoryStats(buildDirectory) {
    if (!buildDirectory || !existsSync(buildDirectory)) {
        return {
            fileCount: 0,
            totalSizeBytes: 0,
            totalSizeInMB: 0,
        };
    }
    const info = {
        totalsize: 0,
        files: [],
    };
    recursivelyCollectFiles(buildDirectory, "", info);
    return {
        fileCount: info.files.length,
        totalSizeBytes: info.totalsize,
        totalSizeInMB: info.totalsize / 1024 / 1024,
    };
}

/** Recursively collect all files in a directory
 * @param {String} directory to search
 * @param {{ time?: string, files: Array<{path:string, hash:string, size:number}>, totalsize:number }} info
 */
function recursivelyCollectFiles(directory, path, info) {
    const entries = fs.readdirSync(directory, { withFileTypes: true });
    for (const entry of entries) {
        if (entry.isDirectory()) {
            // make sure we never collect files inside node_modules
            if (entry.name === "node_modules") {
                needleLog("needle-buildinfo", "Skipping node_modules directory at " + path, "warn", { dimBody: false });
                continue;
            }
            const newPath = path?.length <= 0 ? entry.name : `${path}/${entry.name}`;
            const newDirectory = `${directory}/${entry.name}`;
            recursivelyCollectFiles(newDirectory, newPath, info);
        } else {
            const relpath = path?.length <= 0 ? entry.name : `${path}/${entry.name}`;
            // console.log("[needle-buildinfo] - New File: " + relpath);
            const filehash = crypto.createHash('sha256');
            const fullpath = `${directory}/${entry.name}`;
            filehash.update(fs.readFileSync(fullpath));
            const stats = statSync(fullpath);
            info.files.push({
                path: relpath,
                hash: filehash.digest('hex'),
                size: stats.size
            });
            info.totalsize += stats.size;
        }
    }
}