import SwiftUI
import SwiftData

@MainActor
final class OnboardingViewModel: ObservableObject {
    static let pendingAutoAssignPromptKey = "onboarding_pending_auto_assign_prompt"
    static let pendingPremiumPromptKey = "onboarding_pending_premium_prompt"

    @Published var currentStep: Int = 0
    @Published var selectedMealWindows: MealWindows = .dinnerOnly
    @Published var includeWeekends: Bool = true
    @Published var syncICloud: Bool = true
    @Published var syncCalendar: Bool = false
    @Published var selectedCalendarId: String?
    @Published var lunchTime: Date = {
        var c = DateComponents(); c.hour = 14; c.minute = 0
        return Calendar.current.date(from: c) ?? Date()
    }()
    @Published var dinnerTime: Date = {
        var c = DateComponents(); c.hour = 21; c.minute = 0
        return Calendar.current.date(from: c) ?? Date()
    }()

    // First dishes
    @Published var newDishName: String = ""
    @Published var newDishTags: Set<UUID> = []
    @Published var addedDishes: [(name: String, tagIds: [UUID])] = []
    let maxOnboardingDishes = 10

    let totalSteps = 5

    var canContinue: Bool {
        switch currentStep {
        case 0: return true // Welcome
        case 1: return true // Meal windows (always has selection)
        case 2: return true // Weekends (toggle)
        case 3: return true // Calendar (optional)
        case 4: return addedDishes.count >= 2 // Need at least 2 dishes
        default: return false
        }
    }

    var canAddDish: Bool {
        !newDishName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && canAddMoreDishes
    }

    var canAddMoreDishes: Bool {
        addedDishes.count < maxOnboardingDishes
    }

    func addDish(defaultTagId: UUID? = nil) {
        guard canAddMoreDishes else { return }
        guard canAddDish else { return }
        let normalizedName = newDishName.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !addedDishes.contains(where: { $0.name.lowercased() == normalizedName.lowercased() }) else { return }

        let tagIds: [UUID]
        if !newDishTags.isEmpty {
            tagIds = Array(newDishTags)
        } else if let defaultTagId {
            tagIds = [defaultTagId]
        } else {
            tagIds = []
        }

        addedDishes.append((name: normalizedName, tagIds: tagIds))
        newDishName = ""
        newDishTags = []
    }

    func removeDish(at index: Int) {
        guard addedDishes.indices.contains(index) else { return }
        addedDishes.remove(at: index)
    }

    func addSuggestedDish(name: String, preferredTagNames: [String], availableTags: [Tag]) {
        guard canAddMoreDishes else { return }
        let normalizedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !normalizedName.isEmpty else { return }
        guard !addedDishes.contains(where: { $0.name.lowercased() == normalizedName.lowercased() }) else { return }

        let matchingTags = availableTags.filter { tag in
            preferredTagNames.contains(where: { preferred in
                tag.name.caseInsensitiveCompare(preferred) == .orderedSame ||
                tag.nameEN.caseInsensitiveCompare(preferred) == .orderedSame
            })
        }

        let tagIds: [UUID]
        if !matchingTags.isEmpty {
            tagIds = matchingTags.map(\.id)
        } else if let fallback = availableTags.sorted(by: { $0.sortOrder < $1.sortOrder }).first {
            tagIds = [fallback.id]
        } else {
            tagIds = []
        }

        addedDishes.append((name: normalizedName, tagIds: tagIds))
    }

    func nextStep() {
        if currentStep < totalSteps - 1 {
            withAnimation(.easeInOut(duration: 0.3)) {
                currentStep += 1
            }
        }
    }

    func previousStep() {
        if currentStep > 0 {
            withAnimation(.easeInOut(duration: 0.3)) {
                currentStep -= 1
            }
        }
    }

    @discardableResult
    func completeOnboarding(context: ModelContext) -> Bool {
        // Create or get settings
        let descriptor = FetchDescriptor<AppSettings>()
        let settings = (try? context.fetch(descriptor))?.first ?? {
            let s = AppSettings()
            context.insert(s)
            return s
        }()

        settings.mealWindowsEnum = selectedMealWindows
        settings.includeWeekends = includeWeekends
        settings.iCloudSyncEnabled = syncICloud
        settings.syncEnabled = syncCalendar
        settings.calendarId = selectedCalendarId
        settings.lunchTime = lunchTime
        settings.dinnerTime = dinnerTime
        settings.onboardingCompleted = true

        // Create default tags if not exist
        let tagDescriptor = FetchDescriptor<Tag>()
        if (try? context.fetch(tagDescriptor))?.isEmpty ?? true {
            DefaultDataService.createDefaultTags(context: context)
        }

        // Create dishes
        for dishData in addedDishes {
            let dish = Dish(name: dishData.name, tagIds: dishData.tagIds)
            context.insert(dish)
        }

        // Create first week plan
        let weekStart = Date().startOfWeek()
        let _ = DefaultDataService.createWeekPlan(for: weekStart, settings: settings, context: context)

        do {
            try context.save()
            UserDefaults.standard.set(true, forKey: Self.pendingAutoAssignPromptKey)
            UserDefaults.standard.set(true, forKey: Self.pendingPremiumPromptKey)
            return true
        } catch {
            print("Onboarding save failed: \(error)")
            return false
        }
    }
}
