import SwiftUI

struct WeekCalendarView: View {
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass

    let plan: WeekPlan
    let settings: AppSettings
    let dishes: [Dish]
    let tags: [Tag]
    @ObservedObject var viewModel: HomeViewModel
    var onTapEmptySlot: ((MealSlot) -> Void)?

    private var dayRange: ClosedRange<Int> {
        settings.includeWeekends ? 0...6 : 0...4
    }
    
    private var days: [Int] {
        Array(dayRange)
    }

    private var mealTypes: [MealType] {
        switch settings.mealWindowsEnum {
        case .dinnerOnly: return [.dinner]
        case .lunchOnly: return [.lunch]
        case .both: return [.lunch, .dinner]
        }
    }

    var body: some View {
        Group {
            if horizontalSizeClass == .regular {
                regularGridLayout
            } else {
                compactLayout
            }
        }
    }

    private var compactLayout: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            VStack(spacing: 0) {
                HStack(spacing: 0) {
                    HStack(spacing: 0) {
                        ForEach(Array(days.enumerated()), id: \.element) { index, day in
                            dayHeaderCell(day: day, width: 96, height: 24)
                            if index < days.count - 1 {
                                calendarDivider()
                            }
                        }
                    }
                }

                calendarHorizontalDivider

                ForEach(Array(mealTypes.enumerated()), id: \.element) { index, mealType in
                    HStack(spacing: 0) {
                        ForEach(Array(days.enumerated()), id: \.element) { index, day in
                            let slot = slotFor(day: day, mealType: mealType)
                            draggableSlotView(slot: slot, mealType: mealType)
                                .frame(width: 96)
                            if index < days.count - 1 {
                                calendarDivider()
                            }
                        }
                    }

                    if index < mealTypes.count - 1 {
                        calendarHorizontalDivider
                    }
                }
            }
            .padding(.horizontal, 16)
        }
    }

    private var regularGridLayout: some View {
        GeometryReader { proxy in
            let dayCount = CGFloat(dayRange.count)
            let spacing: CGFloat = 10
            let rowLabelWidth: CGFloat = 86
            let headerHeight: CGFloat = 26
            let slotRowHeight: CGFloat = 96
            let verticalInset: CGFloat = 2
            let contentWidth = proxy.size.width - rowLabelWidth - (dayCount * spacing)
            let dayWidth = max(92, contentWidth / max(dayCount, 1))
            let totalHeight =
                (verticalInset * 2) +
                headerHeight +
                1 +
                (CGFloat(mealTypes.count) * slotRowHeight) +
                CGFloat(max(0, mealTypes.count - 1))

            VStack(spacing: 0) {
                HStack(spacing: 0) {
                    Color.clear.frame(width: rowLabelWidth, height: headerHeight)
                    ForEach(Array(days.enumerated()), id: \.element) { index, day in
                        dayHeaderCell(day: day, width: dayWidth, height: headerHeight)
                        if index < days.count - 1 {
                            calendarDivider(height: headerHeight)
                                .padding(.horizontal, spacing / 2)
                        }
                    }
                }

                calendarHorizontalDivider

                ForEach(Array(mealTypes.enumerated()), id: \.element) { rowIndex, mealType in
                    HStack(spacing: 0) {
                        mealTypeLabel(mealType)
                            .frame(width: rowLabelWidth, height: slotRowHeight)
                            .padding(.trailing, spacing)

                        ForEach(Array(days.enumerated()), id: \.element) { index, day in
                            let slot = slotFor(day: day, mealType: mealType)
                            draggableSlotView(slot: slot, mealType: mealType)
                                .frame(width: dayWidth, height: slotRowHeight)
                            if index < days.count - 1 {
                                calendarDivider(height: slotRowHeight)
                                    .padding(.horizontal, spacing / 2)
                            }
                        }
                    }

                    if rowIndex < mealTypes.count - 1 {
                        calendarHorizontalDivider
                    }
                }
            }
            .padding(.horizontal, 16)
            .padding(.vertical, verticalInset)
            .frame(height: totalHeight, alignment: .top)
        }
        .frame(height: CGFloat(mealTypes.count) * 98 + 44)
    }
    
    private func calendarDivider(height: CGFloat? = nil) -> some View {
        Rectangle()
            .fill(Color.mealMoodTextSecondary.opacity(0.22))
            .frame(width: 1)
            .frame(height: height)
    }

    private var calendarHorizontalDivider: some View {
        Rectangle()
            .fill(Color.mealMoodTextSecondary.opacity(0.22))
            .frame(height: 1)
    }

    private func dayHeader(day: Int) -> some View {
        HStack(spacing: 4) {
            Text(LocalizedStringKey(dayKey(for: day)))
                .font(.mealMoodCaption)
                .foregroundColor(.mealMoodTextPrimary)
                .fontWeight(.semibold)

            let dayDate = plan.weekStartDate.addingDays(day)
            Text("\(Calendar.current.component(.day, from: dayDate))")
                .font(.mealMoodSmall)
                .foregroundColor(dayDate.isToday ? .mealMoodCoral : .mealMoodTextPrimary)
                .fontWeight(dayDate.isToday ? .bold : .regular)
        }
    }

    private func dayHeaderCell(day: Int, width: CGFloat, height: CGFloat) -> some View {
        dayHeader(day: day)
            .frame(width: width, height: height)
            .background(Color.mealMoodCoral.opacity(0.28))
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(Color.mealMoodCoral.opacity(0.45), lineWidth: 1)
            )
            .cornerRadius(8)
    }

    private func mealTypeLabel(_ mealType: MealType) -> some View {
        VStack(spacing: 6) {
            Image(systemName: mealType.icon)
                .font(.system(size: 16, weight: .semibold))
                .foregroundColor(.mealMoodCoral)
            Text(LocalizedStringKey(mealType.rawValue))
                .font(.mealMoodCaption)
                .foregroundColor(.mealMoodTextSecondary)
        }
        .padding(.vertical, 8)
        .background(Color.mealMoodSurface)
        .cornerRadius(10)
    }

    private func slotFor(day: Int, mealType: MealType) -> MealSlot? {
        plan.slots.first { $0.dayOfWeek == day && $0.mealType == mealType.rawValue }
    }

    private func draggableSlotView(slot: MealSlot?, mealType: MealType) -> some View {
        slotView(slot: slot, mealType: mealType)
            .dropDestination(for: String.self) { items, _ in
                guard let payloadRaw = items.first,
                      let payload = parseDropPayload(payloadRaw),
                      let targetSlot = slot,
                      viewModel.canEditCurrentWeek else { return false }

                switch payload {
                case .dish(let dishId):
                    guard let dish = dishes.first(where: { $0.id == dishId }) else { return false }

                    let isValid = AutocompleteEngine.validateDrop(
                        dish: dish, slot: targetSlot, plan: plan,
                        allTags: tags, allDishes: dishes
                    )

                    if isValid {
                        viewModel.assignDish(dish, to: targetSlot, plan: plan, settings: settings)
                        return true
                    }

                    viewModel.confirmInvalidDrop(dish, to: targetSlot, plan: plan, settings: settings)
                    return true
                case .slot(let sourceSlotId):
                    guard let sourceSlot = plan.slots.first(where: { $0.id == sourceSlotId }) else { return false }
                    viewModel.moveOrSwapDish(
                        from: sourceSlot,
                        to: targetSlot,
                        plan: plan,
                        settings: settings,
                        allTags: tags,
                        allDishes: dishes
                    )
                    return true
                }
            }
    }

    @ViewBuilder
    private func slotView(slot: MealSlot?, mealType: MealType) -> some View {
        if let slot = slot, let dishId = slot.dishId,
           let dish = dishes.first(where: { $0.id == dishId }) {
            let dishTags = tags.filter { dish.tagIds.contains($0.id) }
            FilledSlotView(
                dishName: dish.name,
                dishDescription: dish.descriptionText,
                tags: dishTags.map { (name: $0.localizedName(language: settings.languageEnum.resolved()), color: $0.color) },
                mealType: mealType,
                showsRuleWarning: slot.isRuleOverridden,
                onRemove: viewModel.canEditCurrentWeek ? {
                    viewModel.removeDish(from: slot, plan: plan, settings: settings)
                } : nil
            )
            .draggable("slot:\(slot.id.uuidString)")
        } else if let slot = slot {
            EmptySlotView(mealType: mealType)
                .contentShape(Rectangle())
                .onTapGesture {
                    guard viewModel.canEditCurrentWeek else { return }
                    onTapEmptySlot?(slot)
                }
        } else {
            EmptySlotView(mealType: mealType)
        }
    }

    private enum DropPayload {
        case dish(UUID)
        case slot(UUID)
    }

    private func parseDropPayload(_ raw: String) -> DropPayload? {
        let parts = raw.split(separator: ":", maxSplits: 1).map(String.init)
        guard parts.count == 2, let id = UUID(uuidString: parts[1]) else { return nil }
        switch parts[0] {
        case "dish":
            return .dish(id)
        case "slot":
            return .slot(id)
        default:
            // Backward compatibility for older draggable payloads that only sent dish UUID.
            if let fallbackDishId = UUID(uuidString: raw) {
                return .dish(fallbackDishId)
            }
            return nil
        }
    }

    private func dayKey(for dayIndex: Int) -> String {
        switch dayIndex {
        case 0: return "day_mon"
        case 1: return "day_tue"
        case 2: return "day_wed"
        case 3: return "day_thu"
        case 4: return "day_fri"
        case 5: return "day_sat"
        case 6: return "day_sun"
        default: return ""
        }
    }
}
