// node dependencies
const fs = require('fs');
const path = require('path');

// Path to the tasks.json file, which stores all tasks persistently
const tasksFile = path.join(__dirname, 'tasks.json');

module.exports = {
    // In-memory object holding all tasks
    /* Each task has the following structure:
        tasks[taskName] = {
            active: false,       // whether the task is currently running
            timer: null,         // reference to setTimeout for execution
            nextRun: null,       // timestamp of next scheduled execution
            lastRun: null,       // timestamp of last execution
            error: null,         // last error if the task failed
            oneTime: true/false, // if true, task runs only once
            intervalInSec: 0     // interval in seconds for repeated execution
            action: string       // name of the JS file in ROOT/tasks
        }
    */ 
    tasks: {},

    // Load tasks from tasks.json into memory
    loadTasks: function() {
        // Create empty file if missing
        if (!fs.existsSync(tasksFile)) {
            fs.writeFileSync(tasksFile, '{}', 'utf-8');
        }

        // Read and parse JSON
        const rawData = fs.readFileSync(tasksFile, 'utf-8');
        const data = JSON.parse(rawData);

        // Load each task into memory
        for (const name in data) {
            const taskData = data[name];

            // Ensure timer is reset in memory
            taskData.timer = null;

            this.tasks[name] = taskData;

            // If task is active, automatically start the timer
            if (taskData.active) {
                this._startTimer(name);
            }
        }
    },

    // Save current in-memory tasks to tasks.json - Removes timer references before saving
    saveTasks: function() {
        const dataToSave = {};

        for (const name in this.tasks) {
            const task = { ...this.tasks[name] };
            delete task.timer; // timers cannot be persisted
            dataToSave[name] = task;
        }

        fs.writeFileSync(tasksFile, JSON.stringify(dataToSave, null, 4), 'utf-8');
    },

    // Add or update a task in memory and save it to JSON
    // If the task does not exist, initialize default fields
    setTask: function(name, data) {
        // If task does not exist, create default structure
        if (!this.tasks[name]) {
            this.tasks[name] = {
                active: false,
                timer: null,
                nextRun: null,
                lastRun: null,
                error: null,
                oneTime: false,
                intervalInSec: null,
                action: null
            };
        }

        // Merge new data
        Object.assign(this.tasks[name], data);

        // Stop any running timer before updating
        if (this.tasks[name].timer) {
            clearTimeout(this.tasks[name].timer);
            this.tasks[name].timer = null;
        }

        // Immediately start the timer if task is active
        if (this.tasks[name].active) {
            this._startTimer(name);
        }

        // Persist to JSON
        this.saveTasks();
    },

    // Remove a task completely from memory and JSON
    removeTask: function(name) {
        const task = this.tasks[name];

        // Stop running timer if it exists
        if (task && task.timer) clearTimeout(task.timer);

        // Remove from memory
        delete this.tasks[name];

        // Persist changes
        this.saveTasks();
    },

    // Retrieve a single task by name
    getTask: function(name) {
        return this.tasks[name] || null;
    },

    // Retrieve all tasks in memory
    getAllTasks: function() {
        return this.tasks;
    },

    /*
        * Internal function to start the timer for a task
        * - Loads the action from ROOT/tasks
        * - Executes the function at intervals or once depending on task.oneTime
        * @param {string} name - Task name
    */
    _startTimer: function(name) {
        const task = this.tasks[name];

        // Task must exist and have an action defined
        if (!task || !task.action) return;

        // Build path to task action file
        const taskPath = path.join(__dirname, '../../../tasks', task.action + '.js');

        let actionFunc;
        try {
            // Load the exported function from the task file
            actionFunc = require(taskPath);

            if (typeof actionFunc !== 'function') {
                throw new Error('Task action must export a function');
            }
        } catch (err) {
            console.error(`Failed to load action for task "${name}":`, err.message);
            task.error = err.message;
            this.saveTasks();
            return;
        }

        // Convert interval to milliseconds
        const intervalMs = task.intervalInSec ? task.intervalInSec * 1000 : 0;

        // Function executed by the timer
        const executeTask = async () => {
            task.lastRun = Date.now();

            try {
                // Call the action function
                await actionFunc();
                task.error = null;
            } catch (err) {
                task.error = err.message || String(err);
            }

            if (!task.oneTime && intervalMs && task.active) {
                // Recurring task: schedule next run
                task.nextRun = Date.now() + intervalMs;
                task.timer = setTimeout(executeTask, intervalMs);
            } else if (task.oneTime) {
                // One-time task: deactivate after execution
                task.active = false;
                task.nextRun = null;
                task.timer = null;
            }

            // Save updated task info
            this.saveTasks();
        };

        // Activate the task and schedule first execution
        task.active = true;
        task.nextRun = Date.now() + intervalMs;
        task.timer = setTimeout(executeTask, intervalMs);

        // Save memory state
        this.saveTasks();
    }
};