import assert from 'node:assert';
import BoxError from './boxerror.js';
import logger from './logger.js';
import safe from 'safetydance';
import shellModule from './shell.js';

const { log } = logger('df');
const shell = shellModule('df');


// binary units (non SI) 1024 based
function prettyBytes(bytes) {
    assert.strictEqual(typeof bytes, 'number');

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

    return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + '' + sizes[i];
}

function parseLine(line) {
    const parts = line.split(/\s+/, 7); // this way the mountpoint can have spaces in it

    return {
        filesystem: parts[0],
        type: parts[1],
        size: Number.parseInt(parts[2], 10),
        used: Number.parseInt(parts[3], 10),
        available: Number.parseInt(parts[4], 10),
        capacity: Number.parseInt(parts[5], 10) / 100, // note: this has a trailing %
        mountpoint: parts[6]
    };
}

async function filesystems() {
    const [error, output] = await safe(shell.spawn('df', ['-B1', '--output=source,fstype,size,used,avail,pcent,target'], { encoding: 'utf8', timeout: 5000 }));
    if (error) {
        log(`filesystems: df command failed. error: ${error}\n stdout: ${error.stdout}\n stderr: ${error.stderr}`);
        throw new BoxError(BoxError.FS_ERROR, `Error running df: ${error.message}`);
    }

    const lines = output.trim().split('\n').slice(1); // discard header
    const result = [];
    for (const line of lines) {
        result.push(parseLine(line));
    }
    return result;
}

async function file(filename) {
    assert.strictEqual(typeof filename, 'string');

    if (!safe.fs.existsSync(filename)) throw new BoxError(BoxError.NOT_FOUND, `Path ${filename} does not exist`);

    const [error, output] = await safe(shell.spawn('df', ['-B1', '--output=source,fstype,size,used,avail,pcent,target', filename], { encoding: 'utf8', timeout: 5000 }));
    if (error) {
        log(`file: df command failed. error: ${error}\n stdout: ${error.stdout}\n stderr: ${error.stderr}`);
        throw new BoxError(BoxError.FS_ERROR, `Error running df: ${error.message}`);
    }

    const lines = output.trim().split('\n').slice(1); // discard header
    return parseLine(lines[0]);
}

export default {
    filesystems,
    file,
    prettyBytes
};
