const request = require('supertest');
const app = require('../../app');
const { createTestUser } = require('../helpers/testUtils');
const { Task } = require('../../models');
const moment = require('moment-timezone');

describe('Timezone Fixes Integration Tests', () => {
    let testUser;
    let agent;

    beforeEach(async () => {
        // Create test user with specific timezone
        testUser = await createTestUser({
            timezone: 'America/New_York', // EST/EDT timezone
        });

        // Create authenticated agent
        agent = request.agent(app);
        await agent.post('/api/login').send({
            email: testUser.email,
            password: 'password123',
        });
    });

    afterEach(async () => {
        // Clean up test data
        await Task.destroy({ where: { user_id: testUser.id }, force: true });
    });

    describe('Task Creation with Due Dates', () => {
        it('should store due dates in UTC and return them in user timezone', async () => {
            // Create task due Jan 15, 2024 (in user's timezone)
            const createRes = await agent.post('/api/task').send({
                name: 'Test Task',
                due_date: '2024-01-15', // This should be interpreted as Jan 15 in EST
            });

            expect(createRes.statusCode).toBe(201);

            const createdTask = createRes.body;
            expect(createdTask.due_date).toBe('2024-01-15'); // Should return in user timezone format

            // Check that it's stored as UTC in database
            const taskFromDb = await Task.findByPk(createdTask.id);

            // Should be stored as end of day Jan 15 in EST, which is Jan 16 04:59:59 UTC
            expect(taskFromDb.due_date).toBeInstanceOf(Date);
            const storedDateUTC = taskFromDb.due_date.toISOString();
            expect(storedDateUTC).toBe('2024-01-16T04:59:59.999Z');
        });

        it('should handle timezone boundary correctly', async () => {
            // Set user timezone to Pacific (UTC-8)
            await testUser.update({ timezone: 'America/Los_Angeles' });

            const createRes = await agent.post('/api/task').send({
                name: 'Pacific Task',
                due_date: '2024-01-15',
            });

            expect(createRes.statusCode).toBe(201);

            // Check database storage
            const taskFromDb = await Task.findByPk(createRes.body.id);

            // Should be stored as end of day Jan 15 in PST, which is Jan 16 07:59:59 UTC
            const storedDateUTC = taskFromDb.due_date.toISOString();
            expect(storedDateUTC).toBe('2024-01-16T07:59:59.999Z');
        });
    });

    describe('Upcoming Tasks Filter', () => {
        it('should return tasks due within 7 days in user timezone', async () => {
            const userTimezone = 'America/New_York';
            await testUser.update({ timezone: userTimezone });

            // Create tasks with different due dates
            const today = moment.tz(userTimezone).format('YYYY-MM-DD');
            const tomorrow = moment
                .tz(userTimezone)
                .add(1, 'day')
                .format('YYYY-MM-DD');
            const nextWeek = moment
                .tz(userTimezone)
                .add(8, 'days')
                .format('YYYY-MM-DD');

            // Create three tasks
            await agent
                .post('/api/task')
                .send({ name: 'Due Today', due_date: today });

            await agent
                .post('/api/task')
                .send({ name: 'Due Tomorrow', due_date: tomorrow });

            await agent
                .post('/api/task')
                .send({ name: 'Due Next Week', due_date: nextWeek });

            // Fetch upcoming tasks
            const upcomingRes = await agent.get('/api/tasks?type=upcoming');

            expect(upcomingRes.statusCode).toBe(200);

            const upcomingTasks = upcomingRes.body.tasks;

            // Should include tasks due today and tomorrow, but not next week (8 days out)
            const taskNames = upcomingTasks.map((task) => task.name);
            expect(taskNames).toContain('Due Today');
            expect(taskNames).toContain('Due Tomorrow');
            expect(taskNames).not.toContain('Due Next Week');
        });

        it('should return tasks with defer_until within 7 days in user timezone', async () => {
            const userTimezone = 'America/New_York';
            await testUser.update({ timezone: userTimezone });

            // Create tasks with different defer_until dates
            const tomorrow = moment
                .tz(userTimezone)
                .add(1, 'day')
                .format('YYYY-MM-DDTHH:mm:ss');
            const in5Days = moment
                .tz(userTimezone)
                .add(5, 'days')
                .format('YYYY-MM-DDTHH:mm:ss');
            const in10Days = moment
                .tz(userTimezone)
                .add(10, 'days')
                .format('YYYY-MM-DDTHH:mm:ss');

            // Create tasks with defer_until dates (no due_date)
            await agent
                .post('/api/task')
                .send({ name: 'Deferred Tomorrow', defer_until: tomorrow });

            await agent
                .post('/api/task')
                .send({ name: 'Deferred 5 Days', defer_until: in5Days });

            await agent
                .post('/api/task')
                .send({ name: 'Deferred 10 Days', defer_until: in10Days });

            // Fetch upcoming tasks
            const upcomingRes = await agent.get('/api/tasks?type=upcoming');

            expect(upcomingRes.statusCode).toBe(200);

            const upcomingTasks = upcomingRes.body.tasks;
            const taskNames = upcomingTasks.map((task) => task.name);

            // Should include tasks with defer_until in next 7 days
            expect(taskNames).toContain('Deferred Tomorrow');
            expect(taskNames).toContain('Deferred 5 Days');
            // Should NOT include task with defer_until beyond 7 days
            expect(taskNames).not.toContain('Deferred 10 Days');
        });

        it('should handle DST transitions correctly', async () => {
            await testUser.update({ timezone: 'America/New_York' });

            // Create task during DST transition period
            const dstDate = '2024-03-10'; // DST starts March 10, 2024

            await agent
                .post('/api/task')
                .send({ name: 'DST Task', due_date: dstDate });

            const taskRes = await agent.get('/api/tasks');

            expect(taskRes.statusCode).toBe(200);

            const dstTask = taskRes.body.tasks.find(
                (task) => task.name === 'DST Task'
            );
            expect(dstTask.due_date).toBe(dstDate);
        });
    });

    describe('Task Update with Due Dates', () => {
        it('should update due dates correctly with timezone conversion', async () => {
            // Create initial task
            const createRes = await agent
                .post('/api/task')
                .send({ name: 'Update Test Task', due_date: '2024-01-15' });

            const taskId = createRes.body.id;
            const taskUid = createRes.body.uid;

            // Update due date
            const updateRes = await agent
                .patch(`/api/task/${taskUid}`)
                .send({ due_date: '2024-01-20' });

            expect(updateRes.statusCode).toBe(200);
            expect(updateRes.body.due_date).toBe('2024-01-20');

            // Verify database storage
            const taskFromDb = await Task.findByPk(taskId);
            // Should be stored as end of day Jan 20 in EST
            const expectedUTC = moment
                .tz('2024-01-20', 'America/New_York')
                .endOf('day')
                .utc()
                .toDate();
            expect(taskFromDb.due_date.getTime()).toBe(expectedUTC.getTime());
        });

        it('should clear due dates correctly', async () => {
            // Create task with due date
            const createRes = await agent
                .post('/api/task')
                .send({ name: 'Clear Date Task', due_date: '2024-01-15' });

            const taskId = createRes.body.id;
            const taskUid = createRes.body.uid;

            // Clear due date by sending empty string
            const updateRes = await agent
                .patch(`/api/task/${taskUid}`)
                .send({ due_date: '' });

            expect(updateRes.statusCode).toBe(200);
            expect(updateRes.body.due_date).toBeNull();

            // Verify database storage
            const taskFromDb = await Task.findByPk(taskId);
            expect(taskFromDb.due_date).toBeNull();
        });
    });

    describe('Task Metrics with Timezone', () => {
        beforeAll(() => {
            // Mock current time for consistent testing
            jest.useFakeTimers();
            jest.setSystemTime(new Date('2024-01-15T17:00:00Z')); // 12 PM EST
        });

        afterAll(() => {
            jest.useRealTimers();
        });

        it('should calculate "today" tasks based on user timezone', async () => {
            await testUser.update({ timezone: 'America/New_York' });

            // Create task due "today" in user's timezone
            const todayInEST = moment
                .tz('America/New_York')
                .format('YYYY-MM-DD');

            await agent
                .post('/api/task')
                .send({ name: 'Today Task', due_date: todayInEST });

            // Create task due "yesterday" in user's timezone
            const yesterdayInEST = moment
                .tz('America/New_York')
                .subtract(1, 'day')
                .format('YYYY-MM-DD');

            await agent
                .post('/api/task')
                .send({ name: 'Yesterday Task', due_date: yesterdayInEST });

            // Fetch tasks with dashboard lists
            const tasksRes = await agent.get(
                '/api/tasks?type=today&include_lists=true'
            );

            expect(tasksRes.statusCode).toBe(200);

            expect(tasksRes.body.tasks_due_today.length).toBeGreaterThanOrEqual(
                1
            );

            const dueTodayNames = tasksRes.body.tasks_due_today.map(
                (task) => task.name
            );
            expect(dueTodayNames).toContain('Today Task');
            expect(tasksRes.body.tasks_overdue.length).toBeGreaterThanOrEqual(
                1
            );

            const overdueNames = tasksRes.body.tasks_overdue.map(
                (task) => task.name
            );
            expect(overdueNames).toContain('Yesterday Task');
        });
    });

    describe('Cross-timezone Edge Cases', () => {
        it('should handle international date line correctly', async () => {
            // Set timezone to Samoa (UTC+13)
            await testUser.update({ timezone: 'Pacific/Apia' });

            const samoaDate = '2024-01-15';

            const createRes = await agent
                .post('/api/task')
                .send({ name: 'Samoa Task', due_date: samoaDate });

            expect(createRes.statusCode).toBe(201);
            expect(createRes.body.due_date).toBe(samoaDate);

            // Check database storage - should be same day in UTC due to timezone offset
            const taskFromDb = await Task.findByPk(createRes.body.id);
            const storedDateUTC = taskFromDb.due_date.toISOString();
            // End of Jan 15 in Samoa (+13) is Jan 15 10:59:59 UTC
            expect(storedDateUTC).toBe('2024-01-15T10:59:59.999Z');
        });

        it('should handle invalid timezones gracefully', async () => {
            // Set invalid timezone
            await testUser.update({ timezone: 'Invalid/Timezone' });

            const createRes = await agent
                .post('/api/task')
                .send({ name: 'Invalid TZ Task', due_date: '2024-01-15' });

            expect(createRes.statusCode).toBe(201);
            // Should fallback to UTC
            expect(createRes.body.due_date).toBe('2024-01-15');

            // Check that it was stored using UTC fallback
            const taskFromDb = await Task.findByPk(createRes.body.id);
            const storedDateUTC = taskFromDb.due_date.toISOString();
            // End of Jan 15 in UTC
            expect(storedDateUTC).toBe('2024-01-15T23:59:59.999Z');
        });
    });
});
