import SwiftUI
import ComposeApp

/// Tab selection for task browsing
enum OnboardingTaskTab: String, CaseIterable {
    case forYou = "For You"
    case browse = "Browse All"
}

/// First-task onboarding content — pure server-driven, no hardcoded catalog.
///
/// The "For You" tab shows scored suggestions from
/// `GET /api/tasks/suggestions/`. The "Browse All" tab shows the full
/// template catalog from `GET /api/tasks/templates/grouped/`. Both tabs
/// render loading, error, and empty states with a Retry + Skip option —
/// if the network is flaky, users can still finish onboarding and add
/// tasks later.
///
/// Task creation goes through the single transactional bulk endpoint,
/// so either every selection lands or none do.
struct OnboardingFirstTaskContent: View {
    var residenceName: String
    var onTaskAdded: () -> Void

    @StateObject private var vm: OnboardingTasksViewModel
    @ObservedObject private var onboardingState = OnboardingState.shared

    /// Default init used by the app. The VM starts empty and loads via
    /// `loadSuggestions` / `loadGrouped` in `.task`.
    init(residenceName: String, onTaskAdded: @escaping () -> Void) {
        self.residenceName = residenceName
        self.onTaskAdded = onTaskAdded
        self._vm = StateObject(wrappedValue: OnboardingTasksViewModel())
    }

    /// Snapshot-test / preview init that accepts a pre-seeded VM.
    /// Passing a VM built with `initialSuggestions: <fixture>` lets the
    /// "For You" tab render populated content on the first composition
    /// frame, bypassing the APILayer round-trip that fails hermetically.
    init(
        residenceName: String,
        onTaskAdded: @escaping () -> Void,
        viewModel: OnboardingTasksViewModel
    ) {
        self.residenceName = residenceName
        self.onTaskAdded = onTaskAdded
        self._vm = StateObject(wrappedValue: viewModel)
    }

    @State private var selectedIds: Set<Int32> = []
    @State private var selectedTab: OnboardingTaskTab = .forYou
    @State private var expandedCategoryIds: Set<Int32> = []
    @State private var isAnimating = false

    @Environment(\.colorScheme) var colorScheme

    private var selectedCount: Int { selectedIds.count }

    /// Category colour palette, rotated deterministically by category id so
    /// each category card has visual distinction without coupling to names.
    private static let categoryPalette: [Color] = [
        .appPrimary,
        .appAccent,
        .appSecondary,
        Color(hex: "#34C759") ?? .green,
        Color(hex: "#AF52DE") ?? .purple
    ]

    /// Icon derivation — hand-picked SF Symbols bucketed by category-name
    /// keyword. Falls back to `wrench.and.screwdriver.fill`.
    private static func icon(for name: String) -> String {
        let lower = name.lowercased()
        if lower.contains("hvac") || lower.contains("climate") { return "thermometer.medium" }
        if lower.contains("safety") || lower.contains("security") { return "shield.checkered" }
        if lower.contains("plumb") || lower.contains("water") { return "drop.fill" }
        if lower.contains("outdoor") || lower.contains("yard") || lower.contains("landscap") { return "leaf.fill" }
        if lower.contains("appliance") || lower.contains("kitchen") { return "refrigerator.fill" }
        if lower.contains("exterior") || lower.contains("roof") { return "house.lodge.fill" }
        if lower.contains("interior") { return "house.fill" }
        if lower.contains("electric") { return "bolt.fill" }
        if lower.contains("clean") { return "sparkles" }
        return "wrench.and.screwdriver.fill"
    }

    private static func color(for categoryId: Int32?) -> Color {
        let palette = Self.categoryPalette
        let id = categoryId ?? 0
        let index = Int(abs(Int64(id))) % palette.count
        return palette[index]
    }

    /// Groups built from the server's grouped-templates response. Empty
    /// until the catalog loads; the view shows its own loading / error
    /// states so an empty array here is safe to render as nothing.
    private var browseCategories: [OnboardingTaskCategory] {
        guard let grouped = vm.grouped else { return [] }
        return grouped.categories.map { group in
            // Fall back to a negative hash-derived id for "Uncategorized"
            // so the view's Identifiable conformance stays stable without
            // colliding with any real category id.
            let rawId = group.categoryId?.int32Value
            let stableId: Int32 = rawId ?? Int32(truncatingIfNeeded: abs(group.categoryName.hashValue)) * -1
            return OnboardingTaskCategory(
                id: stableId,
                name: group.categoryName.isEmpty ? "Uncategorized" : group.categoryName,
                icon: Self.icon(for: group.categoryName),
                color: Self.color(for: rawId),
                tasks: group.templates.map { Self.template(from: $0, categoryName: group.categoryName) }
            )
        }
    }

    /// Flat template lookup keyed by backend id. Drives the submit path
    /// without caring which tab supplied the selection.
    private var templateLookup: [Int32: OnboardingTaskTemplate] {
        var map: [Int32: OnboardingTaskTemplate] = [:]
        for category in browseCategories {
            for template in category.tasks { map[template.id] = template }
        }
        for suggestion in vm.suggestions {
            let t = suggestion.template
            if map[t.id] == nil {
                map[t.id] = Self.template(
                    from: t,
                    categoryName: t.category?.name ?? "Suggested"
                )
            }
        }
        return map
    }

    private static func template(from t: TaskTemplate, categoryName: String) -> OnboardingTaskTemplate {
        OnboardingTaskTemplate(
            id: t.id,
            title: t.title,
            description: t.description_.isEmpty ? nil : t.description_,
            categoryId: t.categoryId?.int32Value,
            frequencyId: t.frequencyId?.int32Value,
            frequencyLabel: t.frequency?.displayName ?? "One time",
            icon: t.iconIos.isEmpty ? Self.icon(for: categoryName) : t.iconIos,
            color: Self.color(for: t.categoryId?.int32Value)
        )
    }

    var body: some View {
        ZStack {
            WarmGradientBackground()
                .a11yDecorative()

            backgroundBlobs
                .a11yDecorative()

            VStack(spacing: 0) {
                ScrollView(showsIndicators: false) {
                    VStack(spacing: OrganicSpacing.comfortable) {
                        headerBlock
                        selectionChip

                        OnboardingTaskTabBar(selectedTab: $selectedTab)
                            .padding(.horizontal, OrganicSpacing.comfortable)

                        switch selectedTab {
                        case .forYou:
                            forYouTab
                                .padding(.horizontal, OrganicSpacing.comfortable)
                        case .browse:
                            browseTab
                                .padding(.horizontal, OrganicSpacing.comfortable)
                        }
                    }
                    .padding(.bottom, 140) // Space for bottom action
                }

                bottomActionArea
            }

            if let submitError = vm.submitError {
                bannerError(submitError)
            }
        }
        .onAppear {
            isAnimating = true
            Task { await loadInitial() }
        }
        .onDisappear {
            isAnimating = false
        }
    }

    // MARK: - Sub-views

    private var headerBlock: some View {
        VStack(spacing: 16) {
            ZStack {
                Circle()
                    .fill(
                        RadialGradient(
                            colors: [Color.appPrimary.opacity(0.15), Color.clear],
                            center: .center,
                            startRadius: 30,
                            endRadius: 80
                        )
                    )
                    .frame(width: 140, height: 140)
                    .offset(x: -15, y: -15)
                    .scaleEffect(isAnimating ? 1.1 : 1.0)
                    .animation(
                        isAnimating ? Animation.easeInOut(duration: 2.5).repeatForever(autoreverses: true) : .default,
                        value: isAnimating
                    )

                Circle()
                    .fill(
                        RadialGradient(
                            colors: [Color.appAccent.opacity(0.15), Color.clear],
                            center: .center,
                            startRadius: 30,
                            endRadius: 80
                        )
                    )
                    .frame(width: 140, height: 140)
                    .offset(x: 15, y: 15)

                ZStack {
                    Circle()
                        .fill(
                            LinearGradient(
                                colors: [Color.appPrimary, Color.appSecondary],
                                startPoint: .topLeading,
                                endPoint: .bottomTrailing
                            )
                        )
                        .frame(width: 80, height: 80)

                    Image(systemName: "party.popper.fill")
                        .font(.system(size: 36))
                        .foregroundColor(.white)
                }
                .naturalShadow(.pronounced)
            }

            Text("You're all set up!")
                .font(.system(size: 26, weight: .bold, design: .rounded))
                .foregroundColor(Color.appTextPrimary)
                .a11yHeader()

            Text("Let's get you started with some tasks.\nThe more you pick, the more we'll help you remember!")
                .font(.system(size: 15, weight: .medium))
                .foregroundColor(Color.appTextSecondary)
                .multilineTextAlignment(.center)
                .lineSpacing(4)
        }
        .padding(.top, OrganicSpacing.comfortable)
    }

    private var selectionChip: some View {
        HStack(spacing: 8) {
            Image(systemName: selectedCount > 0 ? "checkmark.seal.fill" : "checkmark.circle.fill")
                .foregroundColor(selectedCount > 0 ? Color.appAccent : Color.appPrimary)

            Text("\(selectedCount) task\(selectedCount == 1 ? "" : "s") selected")
                .font(.system(size: 14, weight: .semibold))
                .foregroundColor(Color.appPrimary)
        }
        .padding(.horizontal, 18)
        .padding(.vertical, 10)
        .background(Color.appPrimary.opacity(0.1))
        .clipShape(Capsule())
        .animation(.spring(response: 0.3), value: selectedCount)
        .accessibilityLabel("\(selectedCount) task\(selectedCount == 1 ? "" : "s") selected")
    }

    @ViewBuilder
    private var forYouTab: some View {
        if vm.isLoadingSuggestions {
            OnboardingLoadingPane(message: "Finding tasks for your home...")
        } else if let errorMessage = vm.suggestionsError {
            OnboardingErrorPane(
                headline: "Couldn't load your suggestions",
                message: errorMessage,
                retry: retrySuggestions,
                skip: { skip(reason: "network_error_for_you") },
                secondary: vm.grouped != nil ? .init(label: "Browse All", action: { selectedTab = .browse }) : nil
            )
        } else if vm.suggestions.isEmpty && vm.suggestionsAttempted {
            OnboardingEmptyPane(
                message: "No personalised suggestions yet — browse the full catalog or skip this step.",
                primary: .init(label: vm.grouped != nil ? "Browse All" : "Skip", action: {
                    if vm.grouped != nil {
                        selectedTab = .browse
                    } else {
                        skip(reason: "no_suggestions_no_catalog")
                    }
                })
            )
        } else {
            VStack(spacing: 0) {
                ForEach(vm.suggestions, id: \.template.id) { suggestion in
                    let isSelected = selectedIds.contains(suggestion.template.id)
                    OnboardingSuggestionRow(
                        suggestion: suggestion,
                        isSelected: isSelected,
                        onTap: {
                            toggleSuggestion(suggestion, wasSelected: isSelected)
                        }
                    )
                    if suggestion.template.id != vm.suggestions.last?.template.id {
                        Divider().padding(.leading, 60)
                    }
                }
            }
            .background(Color.appBackgroundSecondary)
            .clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
            .naturalShadow(.subtle)
        }
    }

    @ViewBuilder
    private var browseTab: some View {
        if vm.isLoadingGrouped && vm.grouped == nil {
            OnboardingLoadingPane(message: "Loading the task catalog...")
        } else if let errorMessage = vm.groupedError, vm.grouped == nil {
            OnboardingErrorPane(
                headline: "Couldn't load the task catalog",
                message: errorMessage,
                retry: retryGrouped,
                skip: { skip(reason: "network_error_browse") }
            )
        } else if browseCategories.isEmpty {
            OnboardingEmptyPane(
                message: "No templates available right now.",
                primary: .init(label: "Skip", action: { skip(reason: "empty_catalog") })
            )
        } else {
            VStack(spacing: 12) {
                ForEach(browseCategories) { category in
                    OnboardingCategorySection(
                        category: category,
                        selectedIds: selectedIds,
                        isExpanded: expandedCategoryIds.contains(category.id),
                        onToggleExpand: {
                            withAnimation(.spring(response: 0.3)) {
                                if expandedCategoryIds.contains(category.id) {
                                    expandedCategoryIds.remove(category.id)
                                } else {
                                    expandedCategoryIds.insert(category.id)
                                }
                            }
                        },
                        onToggleTask: { template in
                            toggleBrowse(template)
                        }
                    )
                }
            }
        }
    }

    private var bottomActionArea: some View {
        VStack(spacing: 14) {
            Button(action: submit) {
                HStack(spacing: 10) {
                    if vm.isSubmitting {
                        ProgressView()
                            .progressViewStyle(CircularProgressViewStyle(tint: .white))
                    } else {
                        Text(selectedCount > 0
                             ? "Add \(selectedCount) Task\(selectedCount == 1 ? "" : "s") & Continue"
                             : "Skip for Now")
                            .font(.system(size: 17, weight: .bold))

                        Image(systemName: "arrow.right")
                            .font(.system(size: 16, weight: .bold))
                    }
                }
                .frame(maxWidth: .infinity)
                .frame(height: 56)
                .foregroundColor(Color.appTextOnPrimary)
                .background(
                    selectedCount > 0
                        ? AnyShapeStyle(LinearGradient(colors: [Color.appPrimary, Color.appSecondary], startPoint: .leading, endPoint: .trailing))
                        : AnyShapeStyle(Color.appTextSecondary.opacity(0.5))
                )
                .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
                .naturalShadow(selectedCount > 0 ? .medium : .subtle)
            }
            .disabled(vm.isSubmitting)
            .animation(.easeInOut(duration: 0.2), value: selectedCount)
        }
        .padding(.horizontal, OrganicSpacing.comfortable)
        .padding(.bottom, OrganicSpacing.airy)
    }

    private var backgroundBlobs: some View {
        GeometryReader { geo in
            OrganicBlobShape(variation: 1)
                .fill(
                    RadialGradient(
                        colors: [
                            Color.appPrimary.opacity(0.06),
                            Color.appPrimary.opacity(0.01),
                            Color.clear
                        ],
                        center: .center,
                        startRadius: 0,
                        endRadius: geo.size.width * 0.3
                    )
                )
                .frame(width: geo.size.width * 0.5, height: geo.size.height * 0.25)
                .offset(x: -geo.size.width * 0.1, y: geo.size.height * 0.1)
                .blur(radius: 20)

            OrganicBlobShape(variation: 2)
                .fill(
                    RadialGradient(
                        colors: [
                            Color.appAccent.opacity(0.05),
                            Color.appAccent.opacity(0.01),
                            Color.clear
                        ],
                        center: .center,
                        startRadius: 0,
                        endRadius: geo.size.width * 0.25
                    )
                )
                .frame(width: geo.size.width * 0.4, height: geo.size.height * 0.2)
                .offset(x: geo.size.width * 0.65, y: geo.size.height * 0.75)
                .blur(radius: 15)
        }
    }

    private func bannerError(_ message: String) -> some View {
        VStack {
            Spacer()
            Text(message)
                .font(.system(size: 14, weight: .medium))
                .foregroundColor(.white)
                .padding(.horizontal, 16)
                .padding(.vertical, 12)
                .background(Color.appError.opacity(0.95))
                .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
                .padding(.horizontal, 16)
                .padding(.bottom, 80)
                .transition(.move(edge: .bottom).combined(with: .opacity))
        }
    }

    // MARK: - Actions

    private func loadInitial() async {
        // Always hit the catalog endpoint — both tabs benefit (For You falls
        // back to "Browse All" button on empty suggestions).
        async let groupedTask: Void = vm.loadGrouped()
        if let residenceId = onboardingState.createdResidenceId {
            async let suggestionsTask: Void = vm.loadSuggestions(residenceId: residenceId)
            _ = await (groupedTask, suggestionsTask)
        } else {
            _ = await groupedTask
        }

        // Expand the first category by default once the catalog resolves.
        if expandedCategoryIds.isEmpty, let first = browseCategories.first {
            expandedCategoryIds.insert(first.id)
        }
    }

    private func retrySuggestions() {
        guard let residenceId = onboardingState.createdResidenceId else {
            skip(reason: "no_residence")
            return
        }
        Task { await vm.loadSuggestions(residenceId: residenceId) }
    }

    private func retryGrouped() {
        Task { await vm.loadGrouped(forceRefresh: true) }
    }

    private func skip(reason: String) {
        AnalyticsManager.shared.track(.onboardingTaskStepSkipped(reason: reason))
        onTaskAdded()
    }

    private func toggleSuggestion(_ suggestion: TaskSuggestionResponse, wasSelected: Bool) {
        withAnimation(.spring(response: 0.2)) {
            let id = suggestion.template.id
            if wasSelected {
                selectedIds.remove(id)
            } else {
                selectedIds.insert(id)
                AnalyticsManager.shared.track(.onboardingSuggestionAccepted(
                    templateId: id,
                    relevanceScore: suggestion.relevanceScore
                ))
            }
        }
    }

    private func toggleBrowse(_ template: OnboardingTaskTemplate) {
        withAnimation(.spring(response: 0.2)) {
            if selectedIds.contains(template.id) {
                selectedIds.remove(template.id)
            } else {
                selectedIds.insert(template.id)
                AnalyticsManager.shared.track(.onboardingBrowseTemplateAccepted(
                    templateId: template.id,
                    categoryId: template.categoryId
                ))
            }
        }
    }

    private func submit() {
        // Zero selection → pure skip path, no network call.
        if selectedIds.isEmpty {
            skip(reason: "user_skip")
            return
        }

        guard let residenceId = onboardingState.createdResidenceId else {
            // No residence means onboarding partially failed earlier; just
            // advance so the user isn't stuck.
            skip(reason: "no_residence")
            return
        }

        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        dateFormatter.dateFormat = "yyyy-MM-dd"
        let todayString = dateFormatter.string(from: Date())

        let lookup = templateLookup
        let requests: [TaskCreateRequest] = selectedIds.compactMap { id in
            guard let template = lookup[id] else { return nil }
            return TaskCreateRequest(
                residenceId: residenceId,
                title: template.title,
                description: template.description,
                categoryId: template.categoryId.map { KotlinInt(int: $0) },
                priorityId: nil,
                inProgress: false,
                frequencyId: template.frequencyId.map { KotlinInt(int: $0) },
                customIntervalDays: nil,
                assignedToId: nil,
                dueDate: todayString,
                estimatedCost: nil,
                contractorId: nil,
                templateId: KotlinInt(int: template.id)
            )
        }

        Task {
            let ok = await vm.submit(residenceId: residenceId, requests: requests)
            if ok {
                onTaskAdded()
            }
            // On failure vm.submitError is displayed by the overlay banner;
            // the user can retry by tapping the button again.
        }
    }
}

// MARK: - Adapter models

struct OnboardingTaskCategory: Identifiable {
    let id: Int32
    let name: String
    let icon: String
    let color: Color
    let tasks: [OnboardingTaskTemplate]
}

struct OnboardingTaskTemplate: Identifiable {
    let id: Int32                 // backend TaskTemplate.id — sent as template_id
    let title: String
    let description: String?
    let categoryId: Int32?
    let frequencyId: Int32?
    let frequencyLabel: String
    let icon: String
    let color: Color
}

// MARK: - Tab bar

private struct OnboardingTaskTabBar: View {
    @Binding var selectedTab: OnboardingTaskTab

    var body: some View {
        Picker("", selection: $selectedTab) {
            ForEach(OnboardingTaskTab.allCases, id: \.self) { tab in
                Text(tab.rawValue).tag(tab)
            }
        }
        .pickerStyle(.segmented)
    }
}

// MARK: - Suggestion row (For You)

private struct OnboardingSuggestionRow: View {
    let suggestion: TaskSuggestionResponse
    let isSelected: Bool
    var onTap: () -> Void

    private var relevancePercent: Int {
        Int((suggestion.relevanceScore * 100).rounded())
    }

    var body: some View {
        Button(action: onTap) {
            HStack(spacing: 14) {
                ZStack {
                    Circle()
                        .stroke(isSelected ? Color.appPrimary : Color.appTextSecondary.opacity(0.3), lineWidth: 2)
                        .frame(width: 28, height: 28)
                    if isSelected {
                        Circle()
                            .fill(Color.appPrimary)
                            .frame(width: 28, height: 28)
                        Image(systemName: "checkmark")
                            .font(.system(size: 12, weight: .bold))
                            .foregroundColor(.white)
                    }
                }

                VStack(alignment: .leading, spacing: 2) {
                    Text(suggestion.template.title)
                        .font(.system(size: 15, weight: .medium))
                        .foregroundColor(Color.appTextPrimary)
                    Text(suggestion.template.frequencyDisplay)
                        .font(.system(size: 12, weight: .medium))
                        .foregroundColor(Color.appTextSecondary)
                    if let reason = suggestion.matchReasons.first {
                        Text(reason)
                            .font(.system(size: 11, weight: .semibold))
                            .foregroundColor(Color.appPrimary.opacity(0.85))
                    }
                }

                Spacer()

                Text("\(relevancePercent)%")
                    .font(.system(size: 11, weight: .bold))
                    .foregroundColor(Color.appPrimary)
                    .padding(.horizontal, 8)
                    .padding(.vertical, 4)
                    .background(Color.appPrimary.opacity(0.12))
                    .clipShape(Capsule())
            }
            .padding(.horizontal, 14)
            .padding(.vertical, 12)
            .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .accessibilityLabel("\(suggestion.template.title), \(suggestion.template.frequencyDisplay), \(relevancePercent)% match")
        .accessibilityValue(isSelected ? "selected" : "not selected")
    }
}

// MARK: - Category section (Browse)

private struct OnboardingCategorySection: View {
    let category: OnboardingTaskCategory
    let selectedIds: Set<Int32>
    let isExpanded: Bool
    var onToggleExpand: () -> Void
    var onToggleTask: (OnboardingTaskTemplate) -> Void

    private var selectedInCategory: Int {
        category.tasks.filter { selectedIds.contains($0.id) }.count
    }

    var body: some View {
        VStack(spacing: 0) {
            Button(action: onToggleExpand) {
                HStack(spacing: 14) {
                    ZStack {
                        Circle()
                            .fill(
                                LinearGradient(
                                    colors: [category.color, category.color.opacity(0.7)],
                                    startPoint: .topLeading,
                                    endPoint: .bottomTrailing
                                )
                            )
                            .frame(width: 44, height: 44)
                        Image(systemName: category.icon)
                            .font(.system(size: 18, weight: .medium))
                            .foregroundColor(.white)
                    }
                    .naturalShadow(.subtle)

                    Text(category.name)
                        .font(.system(size: 16, weight: .semibold))
                        .foregroundColor(Color.appTextPrimary)

                    Spacer()

                    if selectedInCategory > 0 {
                        Text("\(selectedInCategory)")
                            .font(.system(size: 12, weight: .bold))
                            .foregroundColor(.white)
                            .frame(width: 24, height: 24)
                            .background(category.color)
                            .clipShape(Circle())
                    }

                    Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
                        .font(.system(size: 12, weight: .semibold))
                        .foregroundColor(Color.appTextSecondary)
                }
                .padding(14)
                .background(Color.appBackgroundSecondary)
                .clipShape(
                    UnevenRoundedRectangle(
                        topLeadingRadius: 18,
                        bottomLeadingRadius: isExpanded ? 0 : 18,
                        bottomTrailingRadius: isExpanded ? 0 : 18,
                        topTrailingRadius: 18,
                        style: .continuous
                    )
                )
            }
            .buttonStyle(.plain)

            if isExpanded {
                VStack(spacing: 0) {
                    ForEach(category.tasks) { task in
                        let isSelected = selectedIds.contains(task.id)
                        OnboardingTemplateRow(
                            template: task,
                            isSelected: isSelected,
                            categoryColor: category.color,
                            onTap: { onToggleTask(task) }
                        )
                        if task.id != category.tasks.last?.id {
                            Divider().padding(.leading, 60)
                        }
                    }
                }
                .background(Color.appBackgroundSecondary.opacity(0.5))
                .clipShape(
                    UnevenRoundedRectangle(
                        topLeadingRadius: 0,
                        bottomLeadingRadius: 18,
                        bottomTrailingRadius: 18,
                        topTrailingRadius: 0,
                        style: .continuous
                    )
                )
            }
        }
        .naturalShadow(.subtle)
    }
}

private struct OnboardingTemplateRow: View {
    let template: OnboardingTaskTemplate
    let isSelected: Bool
    let categoryColor: Color
    var onTap: () -> Void

    var body: some View {
        Button(action: onTap) {
            HStack(spacing: 14) {
                ZStack {
                    Circle()
                        .stroke(isSelected ? categoryColor : Color.appTextSecondary.opacity(0.3), lineWidth: 2)
                        .frame(width: 28, height: 28)
                    if isSelected {
                        Circle()
                            .fill(categoryColor)
                            .frame(width: 28, height: 28)
                        Image(systemName: "checkmark")
                            .font(.system(size: 12, weight: .bold))
                            .foregroundColor(.white)
                    }
                }

                VStack(alignment: .leading, spacing: 2) {
                    Text(template.title)
                        .font(.system(size: 15, weight: .medium))
                        .foregroundColor(Color.appTextPrimary)
                    Text(template.frequencyLabel)
                        .font(.system(size: 12, weight: .medium))
                        .foregroundColor(Color.appTextSecondary)
                }

                Spacer()

                Image(systemName: template.icon)
                    .font(.system(size: 18, weight: .medium))
                    .foregroundColor(categoryColor.opacity(0.6))
            }
            .padding(.horizontal, 14)
            .padding(.vertical, 12)
            .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .accessibilityLabel("\(template.title), \(template.frequencyLabel)")
        .accessibilityValue(isSelected ? "selected" : "not selected")
    }
}

// MARK: - Shared panes

struct OnboardingSecondaryAction {
    let label: String
    let action: () -> Void
}

private struct OnboardingLoadingPane: View {
    let message: String

    var body: some View {
        VStack(spacing: 16) {
            ProgressView()
                .progressViewStyle(CircularProgressViewStyle(tint: Color.appPrimary))
                .scaleEffect(1.2)
            Text(message)
                .font(.system(size: 15, weight: .medium))
                .foregroundColor(Color.appTextSecondary)
                .multilineTextAlignment(.center)
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 40)
    }
}

private struct OnboardingErrorPane: View {
    let headline: String
    let message: String
    var retry: () -> Void
    var skip: () -> Void
    var secondary: OnboardingSecondaryAction?

    var body: some View {
        VStack(spacing: 14) {
            ZStack {
                Circle()
                    .fill(Color.appError.opacity(0.1))
                    .frame(width: 64, height: 64)
                Image(systemName: "wifi.exclamationmark")
                    .font(.system(size: 28))
                    .foregroundColor(Color.appError)
            }

            Text(headline)
                .font(.system(size: 17, weight: .semibold))
                .foregroundColor(Color.appTextPrimary)
                .multilineTextAlignment(.center)

            Text(message)
                .font(.system(size: 14, weight: .medium))
                .foregroundColor(Color.appTextSecondary)
                .multilineTextAlignment(.center)
                .lineSpacing(3)

            HStack(spacing: 10) {
                Button(action: skip) {
                    Text("Skip for now")
                        .font(.system(size: 14, weight: .semibold))
                        .foregroundColor(Color.appTextSecondary)
                        .padding(.horizontal, 16)
                        .padding(.vertical, 10)
                        .background(Color.appTextSecondary.opacity(0.08))
                        .clipShape(Capsule())
                }
                .buttonStyle(.plain)

                if let secondary {
                    Button(action: secondary.action) {
                        Text(secondary.label)
                            .font(.system(size: 14, weight: .semibold))
                            .foregroundColor(Color.appPrimary)
                            .padding(.horizontal, 16)
                            .padding(.vertical, 10)
                            .background(Color.appPrimary.opacity(0.12))
                            .clipShape(Capsule())
                    }
                    .buttonStyle(.plain)
                }

                Button(action: retry) {
                    HStack(spacing: 6) {
                        Image(systemName: "arrow.clockwise")
                            .font(.system(size: 13, weight: .bold))
                        Text("Retry")
                            .font(.system(size: 14, weight: .bold))
                    }
                    .foregroundColor(.white)
                    .padding(.horizontal, 16)
                    .padding(.vertical, 10)
                    .background(Color.appPrimary)
                    .clipShape(Capsule())
                }
                .buttonStyle(.plain)
            }
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 28)
        .padding(.horizontal, 20)
        .background(Color.appBackgroundSecondary)
        .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
        .naturalShadow(.subtle)
    }
}

private struct OnboardingEmptyPane: View {
    let message: String
    let primary: OnboardingSecondaryAction

    var body: some View {
        VStack(spacing: 16) {
            ZStack {
                Circle()
                    .fill(Color.appPrimary.opacity(0.1))
                    .frame(width: 64, height: 64)
                Image(systemName: "tray")
                    .font(.system(size: 28))
                    .foregroundColor(Color.appPrimary)
            }

            Text(message)
                .font(.system(size: 14, weight: .medium))
                .foregroundColor(Color.appTextSecondary)
                .multilineTextAlignment(.center)
                .lineSpacing(3)

            Button(action: primary.action) {
                Text(primary.label)
                    .font(.system(size: 14, weight: .bold))
                    .foregroundColor(.white)
                    .padding(.horizontal, 24)
                    .padding(.vertical, 10)
                    .background(Color.appPrimary)
                    .clipShape(Capsule())
            }
            .buttonStyle(.plain)
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 28)
        .padding(.horizontal, 20)
        .background(Color.appBackgroundSecondary)
        .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
        .naturalShadow(.subtle)
    }
}

// MARK: - Legacy wrapper with navigation bar

struct OnboardingFirstTaskView: View {
    var residenceName: String
    var onTaskAdded: () -> Void
    var onSkip: () -> Void

    var body: some View {
        ZStack {
            WarmGradientBackground()

            VStack(spacing: 0) {
                HStack {
                    Spacer()
                    Button(action: onSkip) {
                        Text("Skip")
                            .font(.system(size: 15, weight: .medium))
                            .foregroundColor(Color.appTextSecondary)
                    }
                }
                .padding(.horizontal, 20)
                .padding(.vertical, 12)

                OnboardingFirstTaskContent(
                    residenceName: residenceName,
                    onTaskAdded: onTaskAdded
                )
            }
        }
    }
}

#Preview {
    OnboardingFirstTaskContent(
        residenceName: "My Home",
        onTaskAdded: {}
    )
}
