const { User, Task, Project, Tag } = require('../../models');
const { Op } = require('sequelize');
const TelegramPoller = require('../telegram/telegramPoller');

// escape markdown special characters
const escapeMarkdown = (text) => {
    if (!text) return '';
    // Characters that need to be escaped in MarkdownV2: \ _*[]()~`>#+-=|{}.!
    // Backslash must be escaped first to avoid double-escaping
    return text
        .toString()
        .replace(/\\/g, '\\\\')
        .replace(/([_*\[\]()~`>#+\-=|{}.!])/g, '\\$1');
};

// get priority emoji
const getPriorityEmoji = (priority) => {
    const emojiMap = {
        2: '🔴', // high
        1: '🟠', // medium
        0: '🟢', // low
    };
    return emojiMap[priority] || '⚪';
};

// create date range for today
const createTodayDateRange = () => {
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const tomorrow = new Date(today);
    tomorrow.setDate(tomorrow.getDate() + 1);
    return { today, tomorrow };
};

// format task for display
const formatTaskForDisplay = (task, index, includeStatus = false) => {
    const priorityEmoji = getPriorityEmoji(task.priority);
    const statusEmoji = includeStatus ? '✅ ' : '';
    const taskName = escapeMarkdown(task.name);
    const projectInfo = task.Project
        ? ` \\[${escapeMarkdown(task.Project.name)}\\]`
        : '';
    return `${index + 1}\\. ${statusEmoji}${priorityEmoji} ${taskName}${projectInfo}\n`;
};

// build task section
const buildTaskSection = (tasks, title, includeStatus = false) => {
    if (tasks.length === 0) return '';

    let section = `${title}\n`;
    section += tasks
        .map((task, index) => formatTaskForDisplay(task, index, includeStatus))
        .join('');
    section += '\n';

    return section;
};

// build summary message
const buildSummaryMessage = (taskSections) => {
    let message = "📋 *Today's Task Summary*\n\n";
    message += '━━━━━━━━━━━━━━━━━━━━━━━━\n\n';
    message += "✏️ *Today's Plan*\n\n";

    message += taskSections.dueToday;
    message += taskSections.inProgress;
    message += taskSections.suggested;
    message += taskSections.completed;

    message += '━━━━━━━━━━━━━━━━━━━━━━━━\n';
    message += '🎯 *Stay focused and make it happen\\!*';

    return message;
};

// calculate next run time
const calculateNextRunTime = (user, fromTime = new Date()) => {
    const frequency = user.task_summary_frequency;
    const from = new Date(fromTime);

    const calculations = {
        daily: () => {
            const nextDay = new Date(from);
            nextDay.setDate(nextDay.getDate() + 1);
            nextDay.setHours(7, 0, 0, 0);
            return nextDay;
        },

        weekdays: () => {
            const currentDay = from.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday
            let daysToAdd = 1;
            if (currentDay === 5) {
                // Friday
                daysToAdd = 3; // Skip to Monday
            } else if (currentDay === 6) {
                // Saturday
                daysToAdd = 2; // Skip to Monday
            }
            const nextWeekday = new Date(from);
            nextWeekday.setDate(nextWeekday.getDate() + daysToAdd);
            nextWeekday.setHours(7, 0, 0, 0);
            return nextWeekday;
        },

        weekly: () => {
            const nextWeek = new Date(from);
            nextWeek.setDate(nextWeek.getDate() + 7);
            nextWeek.setHours(7, 0, 0, 0);
            return nextWeek;
        },

        '1h': () => {
            const nextHour = new Date(from);
            nextHour.setHours(nextHour.getHours() + 1);
            return nextHour;
        },

        '2h': () => {
            const next = new Date(from);
            next.setHours(next.getHours() + 2);
            return next;
        },

        '4h': () => {
            const next = new Date(from);
            next.setHours(next.getHours() + 4);
            return next;
        },

        '8h': () => {
            const next = new Date(from);
            next.setHours(next.getHours() + 8);
            return next;
        },

        '12h': () => {
            const next = new Date(from);
            next.setHours(next.getHours() + 12);
            return next;
        },
    };

    const calculator = calculations[frequency];
    return calculator ? calculator() : calculations.daily();
};

// Side effect function to fetch user by ID
const fetchUser = async (userId) => await User.findByPk(userId);

// Side effect function to fetch due today tasks
const fetchDueTodayTasks = async (userId, today, tomorrow) =>
    await Task.findAll({
        where: {
            user_id: userId,
            due_date: {
                [Op.gte]: today,
                [Op.lt]: tomorrow,
            },
            status: { [Op.ne]: 2 }, // not done
        },
        include: [{ model: Project, attributes: ['name'] }],
        order: [['name', 'ASC']],
    });

// Side effect function to fetch in progress tasks
const fetchInProgressTasks = async (userId) =>
    await Task.findAll({
        where: {
            user_id: userId,
            status: 1, // in_progress
        },
        include: [{ model: Project, attributes: ['name'] }],
        order: [['name', 'ASC']],
    });

// Side effect function to fetch completed today tasks
const fetchCompletedTodayTasks = async (userId, today, tomorrow) =>
    await Task.findAll({
        where: {
            user_id: userId,
            status: 2, // done
            parent_task_id: null,
            updated_at: {
                [Op.gte]: today,
                [Op.lt]: tomorrow,
            },
        },
        include: [{ model: Project, attributes: ['name'] }],
        order: [['name', 'ASC']],
    });

// Side effect function to fetch suggested tasks
const fetchSuggestedTasks = async (userId, excludedIds) => {
    // Create date limit for suggested tasks (30 days from now)
    const maxSuggestionDate = new Date();
    maxSuggestionDate.setDate(maxSuggestionDate.getDate() + 30);

    return await Task.findAll({
        where: {
            user_id: userId,
            status: { [Op.ne]: 2 }, // not done
            id: { [Op.notIn]: excludedIds },
            [Op.or]: [
                { due_date: null }, // tasks without due dates
                { due_date: { [Op.lte]: maxSuggestionDate } }, // tasks due within 30 days
            ],
        },
        include: [{ model: Project, attributes: ['name'] }],
        order: [
            ['priority', 'DESC'],
            ['name', 'ASC'],
        ],
        limit: 5,
    });
};

// Side effect function to send telegram message
const sendTelegramMessage = async (
    token,
    chatId,
    message,
    replyToMessageId = null,
    options = {}
) => {
    const poller = TelegramPoller;
    return await poller.sendTelegramMessage(
        token,
        chatId,
        message,
        replyToMessageId,
        options
    );
};

// Side effect function to update user tracking fields
const updateUserTracking = async (user, lastRun, nextRun) =>
    await user.update({
        task_summary_last_run: lastRun,
        task_summary_next_run: nextRun,
    });

// Function to generate summary for user (contains side effects)
const generateSummaryForUser = async (userId) => {
    try {
        const user = await fetchUser(userId);
        if (!user) return null;

        const { today, tomorrow } = createTodayDateRange();

        // Fetch all task data in parallel
        const [dueToday, inProgress, completedToday] = await Promise.all([
            fetchDueTodayTasks(userId, today, tomorrow),
            fetchInProgressTasks(userId),
            fetchCompletedTodayTasks(userId, today, tomorrow),
        ]);

        // Get suggested tasks (excluding already fetched ones)
        const excludedIds = [
            ...dueToday.map((t) => t.id),
            ...inProgress.map((t) => t.id),
        ];
        const suggestedTasks = await fetchSuggestedTasks(userId, excludedIds);

        // Build task sections
        const taskSections = {
            dueToday: buildTaskSection(dueToday, '🚀 *Tasks Due Today:*'),
            inProgress: buildTaskSection(inProgress, '⚙️ *In Progress Tasks:*'),
            suggested: buildTaskSection(
                suggestedTasks,
                '💡 *Suggested Tasks:*'
            ),
            completed: buildTaskSection(
                completedToday,
                '✅ *Completed Today:*',
                true
            ),
        };

        return buildSummaryMessage(taskSections);
    } catch (error) {
        console.error('Error generating task summary:', error);
        return null;
    }
};

// Function to send summary to user (contains side effects)
const sendSummaryToUser = async (userId) => {
    try {
        const user = await fetchUser(userId);
        if (!user || !user.telegram_bot_token || !user.telegram_chat_id) {
            return false;
        }

        const summary = await generateSummaryForUser(userId);
        if (!summary) return false;

        // Send the message via Telegram with MarkdownV2 formatting
        // If MarkdownV2 parsing fails (e.g. unescaped characters), retry as plain text
        try {
            await sendTelegramMessage(
                user.telegram_bot_token,
                user.telegram_chat_id,
                summary,
                null,
                { parseMode: 'MarkdownV2' }
            );
        } catch (markdownError) {
            console.warn(
                `MarkdownV2 send failed for user ${userId}, retrying as plain text:`,
                markdownError.message
            );
            const plainSummary = summary.replace(
                /\\([_*\[\]()~`>#+\-=|{}.!\\])/g,
                '$1'
            );
            await sendTelegramMessage(
                user.telegram_bot_token,
                user.telegram_chat_id,
                plainSummary
            );
        }

        // Update tracking fields
        const now = new Date();
        const nextRun = calculateNextRunTime(user, now);
        await updateUserTracking(user, now, nextRun);

        return true;
    } catch (error) {
        console.error(
            `Error sending task summary to user ${userId}:`,
            error.message
        );
        return false;
    }
};

// Export functional interface
module.exports = {
    generateSummaryForUser,
    sendSummaryToUser,
    calculateNextRunTime,
    // For testing
    _escapeMarkdown: escapeMarkdown,
    _getPriorityEmoji: getPriorityEmoji,
    _createTodayDateRange: createTodayDateRange,
    _formatTaskForDisplay: formatTaskForDisplay,
    _buildTaskSection: buildTaskSection,
    _buildSummaryMessage: buildSummaryMessage,
};
