import fs from 'fs';
import { FS_ERROR } from '../../interfaces/index.js';

const LOCK_FILE_PATH = "/tmp/sequence"
const SEQUENCE_FILE = "/tmp/sequence"

/**
 * timeout for lock file, we will try to delete it after timeout, in case was not deleted on previous run
 * you can increase this value if you have a lot of processes running on the same machine
 * @type {number}
 * @env TIMEOUT_LOCK_FILE
 */
const TIMEOUT_LOCK_FILE: number = (process.env.TIMEOUT_LOCK_FILE ? ~~process.env.TIMEOUT_LOCK_FILE : undefined) || 10000;

/**
 * set delay in ms
 * @param ms 
 * @returns Promise<void> 
 */
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))

/**
 * create a lock file
 * @param path 
 * @param timeout 
 * @returns Promise<number>
 */
const lockFile = async (path: string, timeout: number = 0): Promise<number> => {
    //TODO if the file exists for long time have to delete it
    // because probably something is broken on previous run
    // this works but may be not the best solution
    if (timeout++ > TIMEOUT_LOCK_FILE) { console.log(timeout, " times loop, try to unlock"); await delay(1000); unlockFile(path) };
    const lockPath = `${path}.lock`
    try {
        return fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_RDWR)
    } catch (error) {
        const err: FS_ERROR = error as FS_ERROR;
        if (err.code !== "EEXIST") console.log(err.message);
        return lockFile(path, timeout)
    }
}

/**
 * unlink the lock file
 * @param path 
 * @param timeout 
 * @returns Promise<void>
 */
const unlockFile = async (path: string, timeout: number = 0): Promise<void> => {
    const lockPath = `${path}.lock`
    if (timeout++ > TIMEOUT_LOCK_FILE) { console.log(timeout, " times loop, unlock exit"); return };
    try {
        return fs.unlinkSync(lockPath)
    }
    catch (error) {
        const err: FS_ERROR = error as FS_ERROR;
        if (err.code === "ENOENT") {
            return;
        } else {
            console.log(err.message);
            await delay(500);
            return unlockFile(path, timeout++);
        }
    }
}

/**
 * update the sequence number on giveb sequence file
 * @param seqfile
 * @param lockfile
 * @returns number
 */
const sequence = (seqfile: string = "", lockfile: string = ""): number => {

    let SEQF = SEQUENCE_FILE;
    let LFP = LOCK_FILE_PATH;
    if (seqfile !== "") SEQF = seqfile;
    if (lockfile !== "") LFP = lockfile;

    if (!fs.existsSync(SEQF)) {
        fs.writeFileSync(SEQF, "0")
    }

    const abla = lockFile(LFP);
    let seq: number = ~~fs.readFileSync(SEQF); //Read as integer ~~ is synonimus for parseInt
    fs.writeFileSync(SEQF, "" + ++seq);
    const oubla = unlockFile(LFP);
    return seq;
}

export default sequence;
