import SwiftUI
import WidgetKit

struct TimetableWeekView: View {
    let entry: TimetableEntry
    @Environment(\.widgetPalette) private var palette

    var body: some View {
        ZStack {
            if !entry.isLoggedIn {
                placeholder("Bitte einloggen, um den Stundenplan zu laden")
            } else if let data = entry.data {
                content(data: data)
            } else {
                placeholder("Lade…")
            }
        }
        .background(MarianumWatermark())
    }

    @ViewBuilder
    private func content(data: WidgetTimetableData) -> some View {
        VStack(alignment: .leading, spacing: 0) {
            header(data: data)
            dayHeaderRow(data: data)
            GeometryReader { geo in
                let totalMin = CGFloat(data.periods.last?.virtualEndMinutes ?? FALLBACK_VIRTUAL_MINUTES)
                let hourHeight = max(
                    MIN_HOUR_HEIGHT,
                    min(MAX_HOUR_HEIGHT, geo.size.height / max(totalMin, 60) * 60)
                )
                let dayColumnWidth = (geo.size.width - 28 - 4) / 5
                // 45pt is the minimum where a 7pt-font room (e.g. "F2.04")
                // plus the subject fits without `minimumScaleFactor`
                // shrinking the subject into illegibility.
                let subjectOnly = dayColumnWidth < 45
                HStack(alignment: .top, spacing: 0) {
                    timeLabelsColumn(hourHeight: hourHeight, periods: data.periods)
                        .frame(width: 28, alignment: .topTrailing)
                    columnDivider
                    ForEach(0..<5, id: \.self) { offset in
                        column(
                            data: data,
                            offset: offset,
                            hourHeight: hourHeight,
                            subjectOnly: subjectOnly
                        )
                        .frame(maxWidth: .infinity)
                        if offset < 4 { columnDivider }
                    }
                }
            }
        }
    }

    private var columnDivider: some View {
        Rectangle()
            .fill(palette.divider)
            .frame(width: 1)
    }

    private func header(data: WidgetTimetableData) -> some View {
        let cal = Calendar.current
        let week = cal.component(.weekOfYear, from: data.anchorDate)
        let endDate = cal.date(byAdding: .day, value: 4, to: data.anchorDate) ?? data.anchorDate
        return HStack {
            Text("KW \(week) · \(shortDate(data.anchorDate))–\(shortDate(endDate))")
                .font(.system(size: 12, weight: .semibold))
                .foregroundStyle(palette.textPrimary)
                .lineLimit(1)
                .minimumScaleFactor(0.8)
            Spacer()
            Text("Stand: \(freshnessLabel(for: data.fetchedAt, relativeTo: entry.date))")
                .font(.system(size: 9))
                .foregroundStyle(palette.textSecondary)
                .lineLimit(1)
        }
    }

    private func dayHeaderRow(data: WidgetTimetableData) -> some View {
        let cal = Calendar.current
        // `.frame(maxWidth: .infinity)` is critical: without an explicit
        // greedy width (or a true `Spacer()` inside), this HStack collapses
        // to the sum of its fixed-width children in a `.leading` VStack
        // and the 5 day-columns end up sharing ~5pt of width — the labels
        // crunch to the left and stop aligning with the grid columns below.
        // The fixed height keeps `columnDivider`'s vertically-flexible
        // Rectangle from stealing space from the GeometryReader.
        return HStack(spacing: 0) {
            Spacer().frame(width: 28)
            columnDivider
            ForEach(0..<5, id: \.self) { offset in
                let day = cal.date(byAdding: .day, value: offset, to: data.anchorDate) ?? data.anchorDate
                VStack(spacing: -1) {
                    Text(weekday(for: day))
                        .font(.system(size: 10, weight: .bold))
                        .foregroundStyle(palette.textPrimary)
                    Text(shortDate(day))
                        .font(.system(size: 8))
                        .foregroundStyle(palette.textSecondary)
                }
                .frame(maxWidth: .infinity)
                if offset < 4 { columnDivider }
            }
        }
        // 20pt fits 10pt weekday + 8pt date with tight -1pt spacing.
        .frame(maxWidth: .infinity, minHeight: 20, maxHeight: 20)
    }

    private func timeLabelsColumn(hourHeight: CGFloat, periods: [WidgetPeriod]) -> some View {
        let totalMin = periods.last?.virtualEndMinutes ?? FALLBACK_VIRTUAL_MINUTES
        let totalHeight = CGFloat(totalMin) * hourHeight / 60.0
        // Mirrors TimeGridView.compactLabels — below 26pt/hour a stacked
        // time+name no longer fits in one period slot and the two labels
        // visually clip into the next slot. Drop to a single bold period
        // number until there's room to read both lines.
        let compact = hourHeight < 26
        return ZStack(alignment: .topTrailing) {
            ForEach(periodBoundaries(periods), id: \.self) { virtualMin in
                Rectangle()
                    .fill(palette.divider)
                    .frame(height: 1)
                    .offset(y: CGFloat(virtualMin) * hourHeight / 60.0)
            }
            ForEach(periods, id: \.startMinutes) { period in
                VStack(alignment: .trailing, spacing: -2) {
                    if compact {
                        Text("\(period.name).")
                            .font(.system(size: 9, weight: .bold))
                            .foregroundStyle(palette.textPrimary)
                            .lineLimit(1)
                    } else {
                        Text(String(format: "%02d:%02d", period.startMinutes / 60, period.startMinutes % 60))
                            .font(.system(size: 9))
                            .foregroundStyle(palette.textPrimary)
                            .lineLimit(1)
                        Text("\(period.name).")
                            .font(.system(size: 7, weight: .bold))
                            .foregroundStyle(palette.textSecondary)
                            .lineLimit(1)
                    }
                }
                .padding(.trailing, 4)
                .offset(y: CGFloat(period.virtualStartMinutes) * hourHeight / 60.0)
            }
        }
        .frame(height: totalHeight, alignment: .topTrailing)
    }

    private func column(
        data: WidgetTimetableData,
        offset: Int,
        hourHeight: CGFloat,
        subjectOnly: Bool
    ) -> some View {
        let cal = Calendar.current
        let day = cal.date(byAdding: .day, value: offset, to: data.anchorDate) ?? data.anchorDate
        let lessonsForDay = data.lessons.filter { cal.isDate($0.start, inSameDayAs: day) }
        return TimeGridView(
            lessons: lessonsForDay,
            periods: data.periods,
            hourHeight: hourHeight,
            showRoom: !subjectOnly,
            showTeacher: !subjectOnly,
            showTimeLabels: false,
            horizontalPadding: 3
        )
    }

    private func weekday(for date: Date) -> String {
        WidgetDateFormatters.weekdayShort.string(from: date)
    }

    private func placeholder(_ message: String) -> some View {
        VStack(spacing: 4) {
            Text("Marianum Stundenplan")
                .font(.system(size: 14, weight: .semibold))
                .foregroundStyle(palette.textPrimary)
            Text(message)
                .font(.caption)
                .foregroundStyle(palette.textSecondary)
                .multilineTextAlignment(.center)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}
