const { RecurringCompletion } = require('../../models');
const { Op } = require('sequelize');

class HabitService {
    /**
     * Log a habit completion
     * Updates streak counters and creates completion record
     */
    async logCompletion(task, completedAt = new Date()) {
        if (!task.habit_mode) {
            throw new Error('Task is not a habit');
        }

        const completion = await RecurringCompletion.create({
            task_id: task.id,
            completed_at: completedAt,
            original_due_date: completedAt, // For habits, due date = completion date
            skipped: false,
        });

        // Update cached counters and mark as done for today
        const updates = await this.calculateStreakUpdates(task, completedAt);
        updates.status = 2; // Mark as done
        updates.completed_at = completedAt;
        await task.update(updates);

        return { completion, task };
    }

    /**
     * Calculate streak updates based on completion
     * This is the core habit logic
     */
    async calculateStreakUpdates(task, completedAt) {
        const updates = {
            habit_total_completions: task.habit_total_completions + 1,
            habit_last_completion_at: completedAt,
        };

        // Calculate new streak
        const newStreak = await this.calculateCurrentStreak(task, completedAt);
        updates.habit_current_streak = newStreak;

        // Update best streak if needed
        if (newStreak > task.habit_best_streak) {
            updates.habit_best_streak = newStreak;
        }

        return updates;
    }

    /**
     * Recalculate all streak values after a completion is deleted
     */
    async recalculateStreaks(task) {
        const completions = await RecurringCompletion.findAll({
            where: {
                task_id: task.id,
                skipped: false,
            },
            order: [['completed_at', 'DESC']],
        });

        const updates = {
            habit_total_completions: completions.length,
            habit_last_completion_at:
                completions.length > 0 ? completions[0].completed_at : null,
        };

        // Calculate current streak
        updates.habit_current_streak =
            completions.length > 0
                ? this.calculateCalendarStreak(completions, new Date())
                : 0;

        // Calculate best streak (need to check all possible streaks)
        updates.habit_best_streak = this.calculateBestStreak(completions);

        return updates;
    }

    /**
     * Calculate the best (longest) streak from completion history
     */
    calculateBestStreak(completions) {
        if (completions.length === 0) return 0;

        let bestStreak = 0;
        let currentStreak = 0;
        let lastDate = null;

        // Sort by date ascending for this calculation
        const sorted = [...completions].sort(
            (a, b) => new Date(a.completed_at) - new Date(b.completed_at)
        );

        for (const completion of sorted) {
            const completedDate = new Date(completion.completed_at);
            completedDate.setHours(0, 0, 0, 0);

            if (!lastDate) {
                currentStreak = 1;
            } else {
                const diffDays = Math.floor(
                    (completedDate - lastDate) / (1000 * 60 * 60 * 24)
                );

                if (diffDays === 1) {
                    // Consecutive day
                    currentStreak++;
                } else if (diffDays === 0) {
                    // Same day, don't change streak
                    continue;
                } else {
                    // Streak broken
                    bestStreak = Math.max(bestStreak, currentStreak);
                    currentStreak = 1;
                }
            }

            lastDate = new Date(completedDate);
        }

        return Math.max(bestStreak, currentStreak);
    }

    /**
     * Calculate current streak based on streak mode
     */
    async calculateCurrentStreak(task, asOfDate = new Date()) {
        const completions = await RecurringCompletion.findAll({
            where: {
                task_id: task.id,
                skipped: false,
            },
            order: [['completed_at', 'DESC']],
        });

        if (completions.length === 0) return 0;

        if (task.habit_streak_mode === 'calendar') {
            return this.calculateCalendarStreak(completions, asOfDate);
        } else {
            return this.calculateScheduledStreak(task, completions, asOfDate);
        }
    }

    /**
     * Calendar streak: consecutive days with completions
     */
    calculateCalendarStreak(completions, asOfDate) {
        let streak = 0;
        let currentDate = new Date(asOfDate);
        currentDate.setHours(0, 0, 0, 0);

        const completionDates = completions.map((c) => {
            const d = new Date(c.completed_at);
            d.setHours(0, 0, 0, 0);
            return d.getTime();
        });

        const uniqueDates = [...new Set(completionDates)].sort((a, b) => b - a);

        for (const dateTimestamp of uniqueDates) {
            const expectedDate = currentDate.getTime();
            if (dateTimestamp === expectedDate) {
                streak++;
                currentDate.setDate(currentDate.getDate() - 1);
            } else if (dateTimestamp < expectedDate) {
                break; // Gap in streak
            }
        }

        return streak;
    }

    /**
     * Scheduled streak: consecutive scheduled occurrences completed
     * Uses recurrence pattern to determine expected dates
     */
    calculateScheduledStreak(task, completions, asOfDate) {
        // Implementation depends on recurrence pattern
        // For MVP: simplified version - count consecutive scheduled periods with completions
        // Full implementation would use recurringTaskService to calculate expected dates

        // Simplified: treat as calendar streak for now
        // TODO: Implement full scheduled streak logic in Phase 2
        return this.calculateCalendarStreak(completions, asOfDate);
    }

    /**
     * Get habit statistics for a period
     */
    async getHabitStats(task, startDate, endDate) {
        const completions = await RecurringCompletion.findAll({
            where: {
                task_id: task.id,
                completed_at: {
                    [Op.between]: [startDate, endDate],
                },
                skipped: false,
            },
            order: [['completed_at', 'ASC']],
        });

        const totalCompletions = completions.length;
        const currentStreak = task.habit_current_streak;

        // Calculate completion rate if target is set
        let completionRate = null;
        if (task.habit_target_count && task.habit_frequency_period) {
            const target = this.calculatePeriodTarget(task, startDate, endDate);
            completionRate = target > 0 ? (totalCompletions / target) * 100 : 0;
        }

        return {
            totalCompletions,
            currentStreak,
            bestStreak: task.habit_best_streak,
            completionRate,
            completions: completions.map((c) => ({
                completed_at: c.completed_at,
                id: c.id,
            })),
        };
    }

    /**
     * Calculate target completions for a date range
     */
    calculatePeriodTarget(task, startDate, endDate) {
        if (!task.habit_target_count || !task.habit_frequency_period) {
            return 0;
        }

        const days = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24));

        switch (task.habit_frequency_period) {
            case 'daily':
                return task.habit_target_count * days;
            case 'weekly':
                return task.habit_target_count * Math.ceil(days / 7);
            case 'monthly':
                return task.habit_target_count * Math.ceil(days / 30);
            default:
                return 0;
        }
    }

    /**
     * Check if habit is due today based on flexibility mode
     */
    isDueToday(task, today = new Date()) {
        if (task.habit_flexibility_mode === 'flexible') {
            // Flexible: always available
            return true;
        } else {
            // Strict: check if today matches recurrence pattern
            // Leverage existing recurringTaskService logic
            const {
                calculateNextDueDate,
            } = require('../tasks/recurringTaskService');
            const nextDue = calculateNextDueDate(
                task,
                task.habit_last_completion_at || task.created_at
            );

            if (!nextDue) return false;

            const todayStart = new Date(today);
            todayStart.setHours(0, 0, 0, 0);
            const todayEnd = new Date(today);
            todayEnd.setHours(23, 59, 59, 999);

            return nextDue >= todayStart && nextDue <= todayEnd;
        }
    }
}

module.exports = new HabitService();
