const { Task, Project } = require('../../models');
const { createTestUser } = require('../helpers/testUtils');
const {
    getTaskMetrics,
} = require('../../modules/tasks/queries/metrics-computation');

const dayFromNow = (days) => new Date(Date.now() + days * 24 * 60 * 60 * 1000);

describe('Task Metrics Suggested Tasks', () => {
    let user;

    const createTask = async (overrides = {}) => {
        const { priority, status, ...rest } = overrides;
        return await Task.create({
            name: rest.name || 'Suggested task',
            user_id: user.id,
            status:
                typeof status === 'string'
                    ? Task.getStatusValue(status)
                    : (status ?? Task.STATUS.NOT_STARTED),
            today: false,
            priority:
                typeof priority === 'string'
                    ? Task.getPriorityValue(priority)
                    : (priority ?? Task.PRIORITY.LOW),
            parent_task_id: null,
            recurring_parent_id: null,
            ...rest,
        });
    };

    beforeEach(async () => {
        user = await createTestUser({ email: 'metrics@example.com' });
    });

    it('orders suggested tasks by priority, due date, and project', async () => {
        const projectAlpha = await Project.create({
            name: 'Alpha Project',
            user_id: user.id,
        });
        const projectBeta = await Project.create({
            name: 'Beta Project',
            user_id: user.id,
        });

        await createTask({
            name: 'High Due Later',
            priority: 'high',
            due_date: dayFromNow(3),
            project_id: projectBeta.id,
        });
        await createTask({
            name: 'High Due Soon',
            priority: 'high',
            due_date: dayFromNow(1),
            project_id: projectAlpha.id,
        });
        await createTask({
            name: 'Medium Alpha',
            priority: 'medium',
            due_date: dayFromNow(4),
            project_id: projectAlpha.id,
        });
        await createTask({
            name: 'Medium Beta',
            priority: 'medium',
            due_date: dayFromNow(4),
            project_id: projectBeta.id,
        });

        const metrics = await getTaskMetrics(user.id, 'UTC');
        const names = metrics.suggested_tasks.map((task) => task.name);
        expect(names).toEqual([
            'High Due Soon',
            'High Due Later',
            'Medium Alpha',
            'Medium Beta',
        ]);
    });

    it('excludes tasks deferred into the future from suggested results', async () => {
        await createTask({ name: 'Ready Task', priority: 'high' });
        await createTask({
            name: 'Deferred Past Task',
            defer_until: dayFromNow(-1),
        });
        await createTask({
            name: 'Deferred Future Task',
            defer_until: dayFromNow(2),
        });

        const metrics = await getTaskMetrics(user.id, 'UTC');
        const names = metrics.suggested_tasks.map((task) => task.name);
        expect(names).toContain('Ready Task');
        expect(names).toContain('Deferred Past Task');
        expect(names).not.toContain('Deferred Future Task');
    });

    it('excludes overdue tasks from suggested results (they show in Overdue section)', async () => {
        // Need at least 3 open tasks for suggested to compute
        await createTask({ name: 'Ready Task 1', priority: 'high' });
        await createTask({ name: 'Ready Task 2', priority: 'medium' });
        await createTask({ name: 'Ready Task 3', priority: 'low' });
        await createTask({
            name: 'Overdue Task',
            due_date: dayFromNow(-3),
        });

        const metrics = await getTaskMetrics(user.id, 'UTC');
        const suggestedNames = metrics.suggested_tasks.map((task) => task.name);
        const overdueNames = metrics.tasks_overdue.map((task) => task.name);

        // Overdue task should be in overdue section, not suggested
        expect(overdueNames).toContain('Overdue Task');
        expect(suggestedNames).not.toContain('Overdue Task');
    });
});

describe('Task Metrics Overdue and Due Today Tasks', () => {
    let user;

    const createTask = async (overrides = {}) => {
        const { priority, status, ...rest } = overrides;
        return await Task.create({
            name: rest.name || 'Test task',
            user_id: user.id,
            status:
                typeof status === 'string'
                    ? Task.getStatusValue(status)
                    : (status ?? Task.STATUS.NOT_STARTED),
            priority:
                typeof priority === 'string'
                    ? Task.getPriorityValue(priority)
                    : (priority ?? Task.PRIORITY.LOW),
            parent_task_id: null,
            recurring_parent_id: null,
            ...rest,
        });
    };

    beforeEach(async () => {
        user = await createTestUser({ email: 'overdue-test@example.com' });
    });

    it('excludes overdue tasks with active status from tasks_overdue (they show in Planned)', async () => {
        // Create an overdue task with IN_PROGRESS status (shows in Planned section)
        await createTask({
            name: 'Overdue In Progress',
            due_date: dayFromNow(-3),
            status: Task.STATUS.IN_PROGRESS,
        });
        // Create a regular overdue task with NOT_STARTED status
        await createTask({
            name: 'Regular Overdue Task',
            due_date: dayFromNow(-2),
            status: Task.STATUS.NOT_STARTED,
        });

        const metrics = await getTaskMetrics(user.id, 'UTC');
        const overdueNames = metrics.tasks_overdue.map((task) => task.name);

        // IN_PROGRESS task should NOT be in overdue (it's in Planned section)
        expect(overdueNames).not.toContain('Overdue In Progress');
        // NOT_STARTED task should be in overdue
        expect(overdueNames).toContain('Regular Overdue Task');
    });

    it('excludes due today tasks with active status from tasks_due_today (they show in Planned)', async () => {
        const today = new Date();
        today.setUTCHours(12, 0, 0, 0);

        // Create a task due today with PLANNED status (shows in Planned section)
        await createTask({
            name: 'Due Today Planned',
            due_date: today,
            status: Task.STATUS.PLANNED,
        });
        // Create a regular due today task with NOT_STARTED status
        await createTask({
            name: 'Regular Due Today',
            due_date: today,
            status: Task.STATUS.NOT_STARTED,
        });

        const metrics = await getTaskMetrics(user.id, 'UTC');
        const dueTodayNames = metrics.tasks_due_today.map((task) => task.name);

        // PLANNED task should NOT be in due today (it's in Planned section)
        expect(dueTodayNames).not.toContain('Due Today Planned');
        // NOT_STARTED task should be in due today
        expect(dueTodayNames).toContain('Regular Due Today');
    });

    it('excludes deferred planned tasks from today_plan until defer time arrives', async () => {
        const tomorrow = dayFromNow(1);
        const yesterday = dayFromNow(-1);

        // Planned task deferred to tomorrow — should NOT be in today_plan
        await createTask({
            name: 'Deferred Future Planned',
            status: Task.STATUS.PLANNED,
            defer_until: tomorrow,
        });

        // Planned task deferred to yesterday — should be in today_plan
        await createTask({
            name: 'Deferred Past Planned',
            status: Task.STATUS.PLANNED,
            defer_until: yesterday,
        });

        // In-progress task deferred to tomorrow — should NOT be in tasks_in_progress
        await createTask({
            name: 'Deferred Future In Progress',
            status: Task.STATUS.IN_PROGRESS,
            defer_until: tomorrow,
        });

        const metrics = await getTaskMetrics(user.id, 'UTC');
        const todayPlanNames = metrics.tasks_today_plan.map(
            (task) => task.name
        );
        const inProgressNames = metrics.tasks_in_progress.map(
            (task) => task.name
        );

        expect(todayPlanNames).toContain('Deferred Past Planned');
        expect(todayPlanNames).not.toContain('Deferred Future Planned');
        expect(inProgressNames).not.toContain('Deferred Future In Progress');
    });

    it('includes tasks with WAITING status in Planned section, not in overdue', async () => {
        await createTask({
            name: 'Overdue Waiting',
            due_date: dayFromNow(-1),
            status: Task.STATUS.WAITING,
        });

        const metrics = await getTaskMetrics(user.id, 'UTC');
        const overdueNames = metrics.tasks_overdue.map((task) => task.name);
        const todayPlanNames = metrics.tasks_today_plan.map(
            (task) => task.name
        );

        // WAITING task should be in today plan, not in overdue
        expect(overdueNames).not.toContain('Overdue Waiting');
        expect(todayPlanNames).toContain('Overdue Waiting');
    });
});
