import { readdir, stat } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

/** 启动`node`进程时所在工作目录的绝对路径 */
const root: string = process.cwd();

/**
 * @description 根据可选的路径片段生成一个新的绝对路径
 * @param dir 路径片段，默认`build`
 * @param metaUrl 模块的完整`url`，如果在`build`目录外调用必传`import.meta.url`
 */
const pathResolve = (dir = '.', metaUrl = import.meta.url) => {
    // 当前文件目录的绝对路径
    const currentFileDir = dirname(fileURLToPath(metaUrl));
    // build 目录的绝对路径
    const buildDir = resolve(currentFileDir, 'build');
    // 解析的绝对路径
    const resolvedPath = resolve(currentFileDir, dir);
    // 检查解析的绝对路径是否在 build 目录内
    if (resolvedPath.startsWith(buildDir)) {
        // 在 build 目录内，返回当前文件路径
        return fileURLToPath(metaUrl);
    }
    // 不在 build 目录内，返回解析后的绝对路径
    return resolvedPath;
};

/** 设置别名 */
const alias: Record<string, string> = {
    '@': pathResolve('../src'),
    '@build': pathResolve(),
};

/** 处理环境变量 */
const wrapperEnv = (envConf: Recordable): ImportMetaEnv => {
    // 默认值
    let ret: ImportMetaEnv = {};

    ret = Object.assign(ret, envConf);

    const setEnv = (envName: string, envValue: string | number | boolean | object) => {
        if (envName.startsWith('VITE_DEV_')) return; // 剔除开发环境变量，不写入 process.env
        if (envName.startsWith('VITE_BUILD_')) return; // 剔除构建环境变量，不写入 process.env
        if (typeof envValue === 'string') {
            process.env[envName] = envValue;
        } else if (typeof envValue === 'number' || typeof envValue === 'boolean') {
            process.env[envName] = String(envValue);
        } else if (typeof envValue === 'object' && envValue !== null) {
            process.env[envName] = JSON.stringify(envValue);
        }
    };

    // 先设置默认值到 process.env
    for (const envName of Object.keys(ret)) {
        setEnv(envName, ret[envName]);
    }

    // 然后用环境变量覆盖默认值
    for (const envName of Object.keys(envConf)) {
        const raw = envConf[envName];
        let envValue: string | number | boolean =
            typeof raw === 'string' ? raw.replace(/\\n/g, '\n') : String(raw);
        envValue =
            envValue === 'true' ? true : envValue === 'false' ? false : envValue;
        if (envName === 'VITE_DEV_PORT') {
            envValue = Number(envValue);
        }
        ret[envName] = envValue;
        setEnv(envName, envValue);
    }
    return ret;
};

const fileListTotal: number[] = [];

/**
 * @description 计算数组元素之和
 * @param arr 数字数组
 * @returns 数组元素之和
 */
const sum = (arr: number[]): number => arr.reduce((a, b) => a + b, 0);

/**
 * @description 将字节单位智能转化成 Bytes 、 KB 、 MB 、 GB 、 TB 、 PB 、 EB 、 ZB 、 YB 其中的一种
 * @param byte — 字节
 * @param digits — 四舍五入保留几位小数（默认四舍五入保留两位小数）
 * @returns — 智能转化字节单位后的值
 */
const formatBytes = (byte: number, digits = 2): string => {
    if (byte === 0) return '0 Bytes';

    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const i = Math.floor(Math.log(byte) / Math.log(k));

    return `${parseFloat((byte / Math.pow(k, i)).toFixed(digits))} ${sizes[i]}`;
};

interface PackageSizeOptions {
    folder?: string;
    callback: (size: string | number) => void;
    format?: boolean;
}

/** 获取指定文件夹中所有文件的总大小 */
const getPackageSize = (options: PackageSizeOptions) => {
    const { folder = 'dist', callback, format = true } = options;
    readdir(folder, (err, files: string[]) => {
        if (err) throw err;
        let count = 0;
        const checkEnd = () => {
            if (++count === files.length) {
                callback(format ? formatBytes(sum(fileListTotal)) : sum(fileListTotal));
            }
        };
        files.forEach((item: string) => {
            stat(`${folder}/${item}`, async (err, stats) => {
                if (err) throw err;
                if (stats.isFile()) {
                    fileListTotal.push(stats.size);
                    checkEnd();
                } else if (stats.isDirectory()) {
                    getPackageSize({
                        folder: `${folder}/${item}/`,
                        callback: checkEnd,
                    });
                }
            });
        });
        if (files.length === 0) {
            callback(0);
        }
    });
};

export { root, pathResolve, alias, wrapperEnv, getPackageSize, formatBytes };
