const { Task, sequelize } = require('../../../models');
const { Op } = require('sequelize');
const moment = require('moment-timezone');
const {
    getSafeTimezone,
    getTodayBoundsInUTC,
} = require('../../../utils/timezone-utils');
const { getTaskIncludeConfigLight } = require('./query-builders');

// Statuses that indicate a task is in the "today plan" (actively being worked on)
// Used to exclude from overdue/due-today sections to avoid duplicates
const TODAY_PLAN_STATUSES = [
    Task.STATUS.IN_PROGRESS,
    Task.STATUS.WAITING,
    Task.STATUS.PLANNED,
    'in_progress',
    'waiting',
    'planned',
];

// Helper to check if a task is in the today plan based on status
function isTaskInTodayPlan(task) {
    return TODAY_PLAN_STATUSES.includes(task.status);
}

async function countTotalOpenTasks(visibleTasksWhere) {
    return await Task.count({
        where: {
            ...visibleTasksWhere,
            status: { [Op.ne]: Task.STATUS.DONE },
            parent_task_id: null,
            recurring_parent_id: null,
        },
        raw: true,
    });
}

async function countTasksPendingOverMonth(visibleTasksWhere) {
    const oneMonthAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
    return await Task.count({
        where: {
            ...visibleTasksWhere,
            status: { [Op.ne]: Task.STATUS.DONE },
            created_at: { [Op.lt]: oneMonthAgo },
            parent_task_id: null,
            recurring_parent_id: null,
        },
        raw: true,
    });
}

async function fetchTasksInProgress(visibleTasksWhere) {
    const now = new Date();
    return await Task.findAll({
        where: {
            ...visibleTasksWhere,
            status: { [Op.in]: [Task.STATUS.IN_PROGRESS, 'in_progress'] },
            // Exclude tasks deferred to the future
            [Op.or]: [
                { defer_until: null },
                { defer_until: { [Op.lte]: now } },
            ],
            parent_task_id: null,
            recurring_parent_id: null,
        },
        include: getTaskIncludeConfigLight(),
        order: [
            ['priority', 'DESC'],
            ['due_date', 'ASC'],
            ['project_id', 'ASC'],
        ],
    });
}

async function fetchTodayPlanTasks(visibleTasksWhere) {
    const now = new Date();
    const todayPlanStatuses = [
        Task.STATUS.IN_PROGRESS,
        Task.STATUS.WAITING,
        Task.STATUS.PLANNED,
        'in_progress',
        'waiting',
        'planned',
    ];

    const excludedStatuses = [
        Task.STATUS.NOT_STARTED,
        Task.STATUS.DONE,
        Task.STATUS.ARCHIVED,
        Task.STATUS.CANCELLED,
        'not_started',
        'done',
        'archived',
        'cancelled',
    ];

    const tasks = await Task.findAll({
        where: {
            [Op.and]: [
                visibleTasksWhere,
                {
                    status: {
                        [Op.in]: todayPlanStatuses,
                        [Op.notIn]: excludedStatuses,
                    },
                    parent_task_id: null,
                },
                // Exclude tasks deferred to the future
                {
                    [Op.or]: [
                        { defer_until: null },
                        { defer_until: { [Op.lte]: now } },
                    ],
                },
                // Include non-recurring tasks, recurring parents, and recurring instances
                {
                    [Op.or]: [
                        {
                            // Non-recurring tasks
                            [Op.and]: [
                                {
                                    [Op.or]: [
                                        { recurrence_type: 'none' },
                                        { recurrence_type: null },
                                        { recurrence_type: '' },
                                    ],
                                },
                                { recurring_parent_id: null },
                            ],
                        },
                        {
                            // Recurring parent tasks
                            [Op.and]: [
                                { recurrence_type: { [Op.ne]: 'none' } },
                                { recurrence_type: { [Op.ne]: null } },
                                { recurrence_type: { [Op.ne]: '' } },
                                { recurring_parent_id: null },
                            ],
                        },
                        {
                            // Recurring instances (not parents)
                            recurring_parent_id: { [Op.ne]: null },
                        },
                    ],
                },
            ],
        },
        include: getTaskIncludeConfigLight(),
        order: [
            ['priority', 'DESC'],
            ['due_date', 'ASC'],
            ['project_id', 'ASC'],
        ],
    });

    // Deduplicate: exclude recurring parents that already have instances in the list
    const parentIdsWithInstances = new Set(
        tasks
            .filter((t) => t.recurring_parent_id)
            .map((t) => t.recurring_parent_id)
    );

    return tasks.filter(
        (t) =>
            !parentIdsWithInstances.has(t.id) || t.recurring_parent_id !== null
    );
}

async function fetchTasksDueToday(visibleTasksWhere, userTimezone) {
    const safeTimezone = getSafeTimezone(userTimezone);
    const todayBounds = getTodayBoundsInUTC(safeTimezone);

    return await Task.findAll({
        where: {
            [Op.and]: [
                visibleTasksWhere,
                {
                    status: {
                        [Op.notIn]: [
                            Task.STATUS.DONE,
                            Task.STATUS.ARCHIVED,
                            Task.STATUS.CANCELLED,
                            'done',
                            'archived',
                            'cancelled',
                            ...TODAY_PLAN_STATUSES,
                        ],
                    },
                    parent_task_id: null,
                    recurring_parent_id: null,
                    [Op.or]: [
                        {
                            due_date: {
                                [Op.and]: [
                                    { [Op.gte]: todayBounds.start },
                                    { [Op.lte]: todayBounds.end },
                                ],
                            },
                        },
                        sequelize.literal(`EXISTS (
          SELECT 1 FROM projects
          WHERE projects.id = Task.project_id
          AND projects.due_date_at >= '${todayBounds.start.toISOString()}'
          AND projects.due_date_at <= '${todayBounds.end.toISOString()}'
        )`),
                    ],
                },
            ],
        },
        include: getTaskIncludeConfigLight(),
        order: [
            ['priority', 'DESC'],
            ['due_date', 'ASC'],
            ['project_id', 'ASC'],
        ],
    });
}

async function fetchOverdueTasks(visibleTasksWhere, userTimezone) {
    const safeTimezone = getSafeTimezone(userTimezone);
    const todayBounds = getTodayBoundsInUTC(safeTimezone);

    return await Task.findAll({
        where: {
            [Op.and]: [
                visibleTasksWhere,
                {
                    status: {
                        [Op.notIn]: [
                            Task.STATUS.DONE,
                            Task.STATUS.ARCHIVED,
                            Task.STATUS.CANCELLED,
                            'done',
                            'archived',
                            'cancelled',
                            // Exclude tasks in today plan (they show in Planned section)
                            ...TODAY_PLAN_STATUSES,
                        ],
                    },
                    parent_task_id: null,
                    recurring_parent_id: null,
                    [Op.or]: [
                        { due_date: { [Op.lt]: todayBounds.start } },
                        sequelize.literal(`EXISTS (
          SELECT 1 FROM projects
          WHERE projects.id = Task.project_id
          AND projects.due_date_at < '${todayBounds.start.toISOString()}'
        )`),
                    ],
                },
            ],
        },
        include: getTaskIncludeConfigLight(),
        order: [
            ['priority', 'DESC'],
            ['due_date', 'ASC'],
            ['project_id', 'ASC'],
        ],
    });
}

async function fetchSomedayTaskIds(userId) {
    return await sequelize
        .query(
            `SELECT DISTINCT task_id FROM tasks_tags
       JOIN tags ON tasks_tags.tag_id = tags.id
       WHERE tags.name = 'someday' AND tags.user_id = ?`,
            {
                replacements: [userId],
                type: sequelize.QueryTypes.SELECT,
            }
        )
        .then((results) => results.map((r) => r.task_id));
}

async function fetchNonProjectTasks(
    visibleTasksWhere,
    excludedTaskIds,
    somedayTaskIds,
    limit = null
) {
    const exclusionIds = [...excludedTaskIds, ...somedayTaskIds];
    const queryOptions = {
        where: {
            ...visibleTasksWhere,
            status: {
                [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING],
            },
            [Op.or]: [{ project_id: null }, { project_id: '' }],
            parent_task_id: null,
            recurring_parent_id: null,
        },
        include: getTaskIncludeConfigLight(),
        order: [
            ['priority', 'DESC'],
            ['due_date', 'ASC'],
            ['project_id', 'ASC'],
        ],
    };

    if (exclusionIds.length > 0) {
        queryOptions.where.id = { [Op.notIn]: exclusionIds };
    }

    if (limit && Number.isInteger(limit)) {
        queryOptions.limit = limit;
    }

    return await Task.findAll(queryOptions);
}

async function fetchProjectTasks(
    visibleTasksWhere,
    excludedTaskIds,
    somedayTaskIds,
    limit = null
) {
    const exclusionIds = [...excludedTaskIds, ...somedayTaskIds];
    const queryOptions = {
        where: {
            ...visibleTasksWhere,
            status: {
                [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING],
            },
            project_id: { [Op.not]: null, [Op.ne]: '' },
            parent_task_id: null,
            recurring_parent_id: null,
        },
        include: getTaskIncludeConfigLight(),
        order: [
            ['priority', 'DESC'],
            ['due_date', 'ASC'],
            ['project_id', 'ASC'],
        ],
    };

    if (exclusionIds.length > 0) {
        queryOptions.where.id = { [Op.notIn]: exclusionIds };
    }

    if (limit && Number.isInteger(limit)) {
        queryOptions.limit = limit;
    }

    return await Task.findAll(queryOptions);
}

async function fetchSomedayFallbackTasks(
    userId,
    usedTaskIds,
    somedayTaskIds,
    limit = null
) {
    if (somedayTaskIds.length === 0) {
        return [];
    }

    const queryOptions = {
        where: {
            user_id: userId,
            status: {
                [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING],
            },
            parent_task_id: null,
            recurring_parent_id: null,
        },
        include: getTaskIncludeConfigLight(),
        order: [
            ['priority', 'DESC'],
            ['due_date', 'ASC'],
            ['project_id', 'ASC'],
        ],
    };

    if (usedTaskIds.length > 0) {
        queryOptions.where.id = {
            [Op.notIn]: usedTaskIds,
            [Op.in]: somedayTaskIds,
        };
    } else {
        queryOptions.where.id = {
            [Op.in]: somedayTaskIds,
        };
    }

    if (limit && Number.isInteger(limit)) {
        queryOptions.limit = limit;
    }

    return await Task.findAll(queryOptions);
}

async function fetchTasksCompletedToday(userId, userTimezone) {
    const safeTimezone = getSafeTimezone(userTimezone);
    const todayBounds = getTodayBoundsInUTC(safeTimezone);

    // Fetch regular completed tasks
    const regularCompletedTasks = await Task.findAll({
        where: {
            user_id: userId,
            status: Task.STATUS.DONE,
            parent_task_id: null,
            recurring_parent_id: null,
            completed_at: {
                [Op.gte]: todayBounds.start,
                [Op.lte]: todayBounds.end,
            },
        },
        include: getTaskIncludeConfigLight(),
    });

    // Fetch recurring tasks completed today via recurring_completions table
    const { RecurringCompletion } = require('../../../models');
    const recurringCompletions = await RecurringCompletion.findAll({
        where: {
            completed_at: {
                [Op.gte]: todayBounds.start,
                [Op.lte]: todayBounds.end,
            },
            skipped: false,
        },
        include: [
            {
                model: Task,
                as: 'Task',
                where: {
                    user_id: userId,
                    parent_task_id: null,
                },
                include: getTaskIncludeConfigLight(),
            },
        ],
    });

    // Extract the tasks from recurring completions and add completed_at and status
    const recurringCompletedTasks = recurringCompletions.map((rc) => {
        const task = rc.Task;
        // Add virtual completed_at and status for display purposes
        task.dataValues.completed_at = rc.completed_at;
        task.dataValues.status = Task.STATUS.DONE;
        // Also set the direct property to ensure it's accessible
        task.status = Task.STATUS.DONE;
        task.completed_at = rc.completed_at;
        return task;
    });

    // Combine both lists
    const allCompletedTasks = [
        ...regularCompletedTasks,
        ...recurringCompletedTasks,
    ];

    // Sort by completed_at DESC
    allCompletedTasks.sort((a, b) => {
        const aTime = a.completed_at || a.dataValues.completed_at;
        const bTime = b.completed_at || b.dataValues.completed_at;
        return new Date(bTime) - new Date(aTime);
    });

    return allCompletedTasks;
}

module.exports = {
    countTotalOpenTasks,
    countTasksPendingOverMonth,
    fetchTasksInProgress,
    fetchTodayPlanTasks,
    fetchTasksDueToday,
    fetchOverdueTasks,
    fetchSomedayTaskIds,
    fetchNonProjectTasks,
    fetchProjectTasks,
    fetchSomedayFallbackTasks,
    fetchTasksCompletedToday,
};
