import { IStorage } from "../storage";
import { Task, InsertTask } from "@shared/schema";
import { addDays, addWeeks, addMonths, addYears } from "date-fns";

export class RecurrenceService {
    private storage: IStorage;

    constructor(storage: IStorage) {
        this.storage = storage;
    }

    async handleTaskCompletion(task: Task): Promise<Task | undefined> {
        if (!task.isRecurring || !task.recurrenceInterval) {
            return;
        }

        // Determine the next due date
        const nextDate = this.calculateNextDate(task);
        if (!nextDate) return;

        // Check if we passed the end date
        if (task.recurrenceEnd && nextDate > task.recurrenceEnd) {
            return;
        }

        // Create the next task
        // Note: startDate is nullable in schema but required in InsertTask type if strict. 
        // We cast to any to avoid strict type issues with recent schema changes if types aren't perfectly synced.
        const newTask: any = {
            title: task.title,
            description: task.description,
            priority: task.priority,
            status: "todo",
            dueDate: nextDate,
            estimatedDuration: task.estimatedDuration,
            labelId: task.labelId,
            projectId: task.projectId,
            energyLevel: task.energyLevel,
            userId: task.userId,

            // Copy recurrence settings
            isRecurring: true,
            recurrenceInterval: task.recurrenceInterval,
            recurrenceIntervalValue: task.recurrenceIntervalValue,
            recurrenceDays: task.recurrenceDays,
            recurrenceEnd: task.recurrenceEnd,

            startDate: null,
        };

        const created = await this.storage.createTask(newTask);

        // Log it
        await this.storage.createAuditLog({
            userId: task.userId!,
            action: "CREATE",
            entityType: "TASK",
            entityId: created.id,
            details: { message: `Recurring task created from ${task.id}`, recurrence: task.recurrenceInterval },
            source: "SYSTEM"
        });

        return created;
    }

    private calculateNextDate(task: Task): Date | null {
        // Base calculation on the original due date or today if missing (though recurring tasks should have due dates)
        const baseDate = task.dueDate ? new Date(task.dueDate) : new Date();
        const intervalValue = task.recurrenceIntervalValue || 1;

        let nextDate: Date = new Date(); // Default init to satisfy typescript, overwritten below

        switch (task.recurrenceInterval) {
            case 'daily':
                nextDate = addDays(baseDate, intervalValue);
                break;
            case 'weekly':
                if (task.recurrenceDays && task.recurrenceDays.length > 0) {
                    // Complex logic for specific days (e.g., Mon, Wed)
                    // Simplified approach for MVP: Outlook style often just means "same day next week" if no days specified.
                    // If days ARE specified, find the next matching day.

                    let potentialDate = addDays(baseDate, 1);
                    // Search for next 14 days maximum to avoid infinite loops
                    let found = false;
                    for (let i = 0; i < 14; i++) {
                        const dayOfWeek = potentialDate.getDay(); // 0=Sun, 1=Mon
                        if (task.recurrenceDays.includes(dayOfWeek)) {
                            nextDate = potentialDate;
                            found = true;
                            break;
                        }
                        potentialDate = addDays(potentialDate, 1);
                    }
                    if (!found) nextDate = addWeeks(baseDate, intervalValue); // Fallback
                } else {
                    nextDate = addWeeks(baseDate, intervalValue);
                }
                break;
            case 'monthly':
                nextDate = addMonths(baseDate, intervalValue);
                break;
            case 'yearly':
                nextDate = addYears(baseDate, intervalValue);
                break;
            default:
                return null;
        }

        return nextDate;
    }
}
