//FIXME: after refactor, move to the correct path
import fs from 'node:fs/promises';
import path from 'node:path';
import { createHash } from 'node:crypto';
import { txEnv } from '../../globalData';

//Hash test
const hashFile = async (filePath: string) => {
    const rawFile = await fs.readFile(filePath, 'utf8')
    const normalized = rawFile.normalize('NFKC')
        .replace(/\r\n/g, '\n')
        .replace(/^\uFEFF/, '');
    return createHash('sha1').update(normalized).digest('hex');
}

// Limits
const MAX_FILES = 300;
const MAX_TOTAL_SIZE = 52_428_800; // 50MB
const MAX_FILE_SIZE = 20_971_520; // 20MB
const MAX_DEPTH = 10;
const MAX_EXECUTION_TIME = 30 * 1000;
const IGNORED_FOLDERS = [
    'db',
    'cache',
    'dist',
    '.reports',
    'license_report',
    'tmp_core_tsc',
    'node_modules',
    'txData',
];


type ContentFileType = {
    path: string;
    size: number;
    hash: string;
}

export default async function scanMonitorFiles() {
    const rootPath = txEnv.txaPath;
    const allFiles: ContentFileType[] = [];
    let totalFiles = 0;
    let totalSize = 0;

    try {
        const tsStart = Date.now();
        const scanDir = async (dir: string, depth: number = 0) => {
            if (depth > MAX_DEPTH) {
                throw new Error('MAX_DEPTH');
            }

            let filesFound = 0;
            const entries = await fs.readdir(dir, { withFileTypes: true });
            for (const entry of entries) {
                if (totalFiles >= MAX_FILES) {
                    throw new Error('MAX_FILES');
                } else if (totalSize >= MAX_TOTAL_SIZE) {
                    throw new Error('MAX_TOTAL_SIZE');
                } else if (Date.now() - tsStart > MAX_EXECUTION_TIME) {
                    throw new Error('MAX_EXECUTION_TIME');
                }

                const entryPath = path.join(dir, entry.name);
                let relativeEntryPath = path.relative(rootPath, entryPath);
                relativeEntryPath = './' + relativeEntryPath.split(path.sep).join(path.posix.sep);

                if (entry.isDirectory()) {
                    if (IGNORED_FOLDERS.includes(entry.name)) {
                        continue;
                    }
                    await scanDir(entryPath, depth + 1);
                } else if (entry.isFile()) {
                    const stats = await fs.stat(entryPath);
                    if (stats.size > MAX_FILE_SIZE) {
                        throw new Error('MAX_SIZE');
                    }

                    allFiles.push({
                        path: relativeEntryPath,
                        size: stats.size,
                        hash: await hashFile(entryPath),
                    });
                    filesFound++;
                    totalFiles++;
                    totalSize += stats.size;
                }
            }
            return filesFound;
        };
        await scanDir(rootPath);
        allFiles.sort((a, b) => a.path.localeCompare(b.path));
        return {
            totalFiles,
            totalSize,
            allFiles,
        };
    } catch (error) {
        //At least saving the progress
        return {
            error: (error as any).message,
            totalFiles,
            totalSize,
            allFiles,
        };
    }
}
