const cron = require('node-cron');
const { User } = require('../../models');
const TaskSummaryService = require('./taskSummaryService');
const { setConfig, getConfig } = require('../../config/config');
const config = getConfig();

const createSchedulerState = () => ({
    jobs: new Map(),
    isInitialized: false,
});

let schedulerState = createSchedulerState();

const shouldDisableScheduler = () =>
    config.environment === 'test' || config.disableScheduler;

const createJobConfig = () => ({
    scheduled: false,
    timezone: 'UTC',
});

const getCronExpression = (frequency) => {
    const expressions = {
        daily: '0 7 * * *',
        weekdays: '0 7 * * 1-5',
        weekly: '0 7 * * 1',
        '1h': '0 * * * *',
        '2h': '0 */2 * * *',
        '4h': '0 */4 * * *',
        '8h': '0 */8 * * *',
        '12h': '0 */12 * * *',
        cleanup_tokens: '0 2 * * *',
        deferred_tasks: '*/5 * * * *',
        due_tasks: '*/15 * * * *',
        due_projects: '*/15 * * * *',
    };
    return expressions[frequency];
};

const createJobHandler = (frequency) => async () => {
    if (frequency === 'cleanup_tokens') {
        await cleanupExpiredTokens();
    } else if (frequency === 'deferred_tasks') {
        await processDeferredTasks();
    } else if (frequency === 'due_tasks') {
        await processDueTasks();
    } else if (frequency === 'due_projects') {
        await processDueProjects();
    } else {
        await processSummariesForFrequency(frequency);
    }
};

const createJobEntries = () => {
    const frequencies = [
        'daily',
        'weekdays',
        'weekly',
        '1h',
        '2h',
        '4h',
        '8h',
        '12h',
        'cleanup_tokens',
        'deferred_tasks',
        'due_tasks',
        'due_projects',
    ];

    return frequencies.map((frequency) => {
        const cronExpression = getCronExpression(frequency);
        const jobHandler = createJobHandler(frequency);
        const jobConfig = createJobConfig();
        const job = cron.schedule(cronExpression, jobHandler, jobConfig);

        return [frequency, job];
    });
};

const startJobs = (jobs) => {
    jobs.forEach((job, frequency) => {
        job.start();
    });
};

const stopJobs = (jobs) => {
    jobs.forEach((job, frequency) => {
        job.stop();
    });
};

const fetchUsersForFrequency = async (frequency) => {
    return await User.findAll({
        where: {
            telegram_bot_token: { [require('sequelize').Op.ne]: null },
            telegram_chat_id: { [require('sequelize').Op.ne]: null },
            task_summary_enabled: true,
            task_summary_frequency: frequency,
        },
    });
};

const sendSummaryToUser = async (userId, frequency) => {
    try {
        const success = await TaskSummaryService.sendSummaryToUser(userId);
        return success;
    } catch (error) {
        return false;
    }
};

const processSummariesForFrequency = async (frequency) => {
    try {
        const users = await fetchUsersForFrequency(frequency);

        const results = await Promise.allSettled(
            users.map((user) => sendSummaryToUser(user.id, frequency))
        );

        return results;
    } catch (error) {
        throw error;
    }
};

const cleanupExpiredTokens = async () => {
    try {
        const {
            cleanupExpiredTokens: cleanup,
        } = require('./registrationService');
        const count = await cleanup();
        return count;
    } catch (error) {
        throw error;
    }
};

const processDeferredTasks = async () => {
    try {
        const { checkDeferredTasks } = require('./deferredTaskService');
        const result = await checkDeferredTasks();
        return result;
    } catch (error) {
        throw error;
    }
};

const processDueTasks = async () => {
    try {
        const { checkDueTasks } = require('./dueTaskService');
        const result = await checkDueTasks();
        return result;
    } catch (error) {
        throw error;
    }
};

const processDueProjects = async () => {
    try {
        const { checkDueProjects } = require('../projects/dueProjectService');
        const result = await checkDueProjects();
        return result;
    } catch (error) {
        throw error;
    }
};

const initialize = async () => {
    if (schedulerState.isInitialized) {
        return schedulerState;
    }

    if (shouldDisableScheduler()) {
        return schedulerState;
    }

    const jobEntries = createJobEntries();
    const jobs = new Map(jobEntries);

    startJobs(jobs);

    schedulerState = {
        jobs,
        isInitialized: true,
    };

    return schedulerState;
};

const stop = async () => {
    if (!schedulerState.isInitialized) {
        return schedulerState;
    }

    stopJobs(schedulerState.jobs);

    schedulerState = createSchedulerState();

    return schedulerState;
};

const restart = async () => {
    await stop();
    return await initialize();
};

const getStatus = () => ({
    initialized: schedulerState.isInitialized,
    jobCount: schedulerState.jobs.size,
    jobs: Array.from(schedulerState.jobs.keys()),
});

module.exports = {
    initialize,
    stop,
    restart,
    getStatus,
    processSummariesForFrequency,
    cleanupExpiredTokens,
    processDeferredTasks,
    processDueTasks,
    processDueProjects,
    _createSchedulerState: createSchedulerState,
    _shouldDisableScheduler: shouldDisableScheduler,
    _getCronExpression: getCronExpression,
};
