package com.tt.honeyDue.ui.screens.onboarding

import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tt.honeyDue.analytics.AnalyticsEvents
import com.tt.honeyDue.analytics.PostHogAnalytics
import com.tt.honeyDue.data.LocalDataManager
import com.tt.honeyDue.models.TaskCreateRequest
import com.tt.honeyDue.models.TaskSuggestionResponse
import com.tt.honeyDue.models.TaskSuggestionsResponse
import com.tt.honeyDue.models.TaskTemplate
import com.tt.honeyDue.models.TaskTemplateCategoryGroup
import com.tt.honeyDue.models.TaskTemplatesGroupedResponse
import com.tt.honeyDue.network.ApiResult
import com.tt.honeyDue.ui.theme.*
import com.tt.honeyDue.viewmodel.OnboardingViewModel
import honeydue.composeapp.generated.resources.*
import com.tt.honeyDue.util.DateUtils
import org.jetbrains.compose.resources.stringResource

// ==================== View-model adapters ====================
// Server templates are mapped into these lightweight Compose-friendly shapes
// so the existing TaskCategorySection / TaskTemplateRow composables don't
// need to know about serialization or icon strings.

data class OnboardingTaskTemplate(
    val id: Int,                // backend TaskTemplate.id — sent to server as template_id
    val title: String,
    val description: String,
    val categoryId: Int?,
    val frequencyId: Int?,
    val frequencyLabel: String, // pre-resolved human label for display
    val icon: ImageVector       // derived from category name; see iconForCategory
)

data class OnboardingTaskCategory(
    val id: Int,                // backend TaskCategory.id, or a stable fallback for Uncategorized
    val name: String,
    val icon: ImageVector,
    val color: Color,
    val tasks: List<OnboardingTaskTemplate>
)

@Composable
fun OnboardingFirstTaskContent(
    viewModel: OnboardingViewModel,
    onTasksAdded: () -> Unit
) {
    val dataManager = LocalDataManager.current
    var selectedBrowseIds by remember { mutableStateOf(setOf<Int>()) }
    var selectedSuggestionIds by remember { mutableStateOf(setOf<Int>()) }
    var expandedCategoryId by remember { mutableStateOf<Int?>(null) }
    var isCreatingTasks by remember { mutableStateOf(false) }
    var selectedTabIndex by remember { mutableStateOf(0) }

    val createTasksState by viewModel.createTasksState.collectAsStateWithLifecycle()
    val suggestionsState by viewModel.suggestionsState.collectAsStateWithLifecycle()
    val templatesGroupedState by viewModel.templatesGroupedState.collectAsStateWithLifecycle()

    // Kick off both network calls on mount. Suggestions needs a residence;
    // the grouped catalog is user-independent and safe to load immediately.
    LaunchedEffect(Unit) {
        val residence = dataManager.residences.value.firstOrNull()
        if (residence != null) {
            viewModel.loadSuggestions(residence.id)
        }
        viewModel.loadTemplatesGrouped()
    }

    // One-shot analytics: fire when suggestions first resolve to Success.
    LaunchedEffect(suggestionsState) {
        (suggestionsState as? ApiResult.Success<TaskSuggestionsResponse>)?.let { s ->
            PostHogAnalytics.capture(
                AnalyticsEvents.ONBOARDING_SUGGESTIONS_LOADED,
                mapOf(
                    "count" to s.data.suggestions.size,
                    "profile_completeness" to s.data.profileCompleteness
                )
            )
        }
    }

    LaunchedEffect(createTasksState) {
        when (val state = createTasksState) {
            is ApiResult.Success -> {
                isCreatingTasks = false
                PostHogAnalytics.capture(
                    AnalyticsEvents.ONBOARDING_TASKS_CREATED,
                    mapOf("count" to (selectedBrowseIds.size + selectedSuggestionIds.size))
                )
                onTasksAdded()
            }
            is ApiResult.Error -> {
                isCreatingTasks = false
                // Surface the failure to the user then still advance — no
                // partial commit reached the server thanks to bulk/transaction.
                onTasksAdded()
            }
            is ApiResult.Loading -> {
                isCreatingTasks = true
            }
            else -> {}
        }
    }

    // Map the grouped-templates response into the adapter shape used by the
    // Browse tab. Re-computed on every emission; the server payload is small.
    val browseCategories: List<OnboardingTaskCategory> = remember(templatesGroupedState) {
        (templatesGroupedState as? ApiResult.Success<TaskTemplatesGroupedResponse>)
            ?.data?.categories.orEmpty()
            .map { it.toAdapter() }
    }
    val allBrowseTasks: List<OnboardingTaskTemplate> = browseCategories.flatMap { it.tasks }
    val totalSelectedCount = selectedBrowseIds.size + selectedSuggestionIds.size

    // First category expanded by default once it resolves.
    LaunchedEffect(browseCategories) {
        if (expandedCategoryId == null) {
            expandedCategoryId = browseCategories.firstOrNull()?.id
        }
    }

    val hasSuggestions = suggestionsState is ApiResult.Success &&
        (suggestionsState as? ApiResult.Success<TaskSuggestionsResponse>)?.data?.suggestions?.isNotEmpty() == true

    val skipOnboarding: (String) -> Unit = { reason ->
        PostHogAnalytics.capture(
            AnalyticsEvents.ONBOARDING_TASK_STEP_SKIPPED,
            mapOf("reason" to reason)
        )
        onTasksAdded()
    }

    Column(modifier = Modifier.fillMaxSize()) {
        // Header (shared across tabs)
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = OrganicSpacing.lg, vertical = OrganicSpacing.md),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            OrganicIconContainer(
                icon = Icons.Default.Celebration,
                size = 80.dp,
                iconSize = 40.dp,
                gradientColors = listOf(
                    MaterialTheme.colorScheme.primary,
                    MaterialTheme.colorScheme.secondary
                ),
                contentDescription = null // decorative
            )

            Spacer(modifier = Modifier.height(OrganicSpacing.lg))

            Text(
                text = stringResource(Res.string.onboarding_tasks_title),
                style = MaterialTheme.typography.headlineMedium,
                fontWeight = FontWeight.Bold,
                color = MaterialTheme.colorScheme.onBackground
            )

            Spacer(modifier = Modifier.height(OrganicSpacing.sm))

            Text(
                text = stringResource(Res.string.onboarding_tasks_subtitle),
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                textAlign = TextAlign.Center
            )

            Spacer(modifier = Modifier.height(OrganicSpacing.lg))

            Surface(
                shape = RoundedCornerShape(OrganicRadius.xl),
                color = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f)
            ) {
                Row(
                    modifier = Modifier.padding(horizontal = OrganicSpacing.lg, vertical = OrganicSpacing.sm),
                    horizontalArrangement = Arrangement.spacedBy(OrganicSpacing.sm),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Icon(
                        imageVector = Icons.Default.CheckCircleOutline,
                        contentDescription = null, // decorative
                        modifier = Modifier.size(20.dp),
                        tint = MaterialTheme.colorScheme.primary
                    )
                    Text(
                        text = "$totalSelectedCount task${if (totalSelectedCount == 1) "" else "s"} selected",
                        style = MaterialTheme.typography.bodyMedium,
                        fontWeight = FontWeight.Medium,
                        color = MaterialTheme.colorScheme.primary
                    )
                }
            }
        }

        // Tab row — shown when suggestions are loading, errored, or present.
        // Hidden only when the screen is pure Browse-only (no home-profile
        // data collected), which iOS doesn't reach in the new flow but kept
        // for safety.
        val showTabs = suggestionsState !is ApiResult.Idle
        if (showTabs) {
            TabRow(
                selectedTabIndex = selectedTabIndex,
                containerColor = MaterialTheme.colorScheme.surface,
                contentColor = MaterialTheme.colorScheme.primary,
                modifier = Modifier.fillMaxWidth()
            ) {
                Tab(
                    selected = selectedTabIndex == 0,
                    onClick = { selectedTabIndex = 0 },
                    text = {
                        Text(
                            text = stringResource(Res.string.for_you_tab),
                            fontWeight = if (selectedTabIndex == 0) FontWeight.SemiBold else FontWeight.Normal
                        )
                    },
                    icon = {
                        Icon(Icons.Default.AutoAwesome, contentDescription = null, modifier = Modifier.size(18.dp) ) // decorative
                    }
                )
                Tab(
                    selected = selectedTabIndex == 1,
                    onClick = { selectedTabIndex = 1 },
                    text = {
                        Text(
                            text = stringResource(Res.string.browse_tab),
                            fontWeight = if (selectedTabIndex == 1) FontWeight.SemiBold else FontWeight.Normal
                        )
                    },
                    icon = {
                        Icon(Icons.Default.ViewList, contentDescription = null, modifier = Modifier.size(18.dp) ) // decorative
                    }
                )
            }
        }

        // Tab content
        val residenceForRetry = dataManager.residences.value.firstOrNull()
        if (selectedTabIndex == 0 && showTabs) {
            ForYouTabContent(
                suggestionsState = suggestionsState,
                selectedSuggestionIds = selectedSuggestionIds,
                hasBrowseFallback = hasSuggestions ||
                    templatesGroupedState is ApiResult.Success,
                onToggleSuggestion = { templateId, relevance ->
                    val wasSelected = templateId in selectedSuggestionIds
                    selectedSuggestionIds = if (wasSelected) {
                        selectedSuggestionIds - templateId
                    } else {
                        PostHogAnalytics.capture(
                            AnalyticsEvents.ONBOARDING_SUGGESTION_ACCEPTED,
                            mapOf("template_id" to templateId, "relevance_score" to relevance)
                        )
                        selectedSuggestionIds + templateId
                    }
                },
                onRetry = { residenceForRetry?.let { viewModel.loadSuggestions(it.id) } },
                onSkip = { skipOnboarding("network_error_for_you") },
                onSwitchToBrowse = { selectedTabIndex = 1 },
                modifier = Modifier.weight(1f)
            )
        } else {
            BrowseTabContent(
                templatesGroupedState = templatesGroupedState,
                browseCategories = browseCategories,
                selectedTaskIds = selectedBrowseIds,
                expandedCategoryId = expandedCategoryId,
                onToggleExpand = { catId ->
                    expandedCategoryId = if (expandedCategoryId == catId) null else catId
                },
                onToggleTask = { template ->
                    val wasSelected = template.id in selectedBrowseIds
                    selectedBrowseIds = if (wasSelected) {
                        selectedBrowseIds - template.id
                    } else {
                        PostHogAnalytics.capture(
                            AnalyticsEvents.ONBOARDING_BROWSE_TEMPLATE_ACCEPTED,
                            mapOf(
                                "template_id" to template.id,
                                "category_id" to (template.categoryId ?: -1)
                            )
                        )
                        selectedBrowseIds + template.id
                    }
                },
                onRetry = { viewModel.loadTemplatesGrouped(forceRefresh = true) },
                onSkip = { skipOnboarding("network_error_browse") },
                modifier = Modifier.weight(1f)
            )
        }

        // Bottom action area (shared)
        Surface(
            modifier = Modifier.fillMaxWidth(),
            shadowElevation = 8.dp
        ) {
            Column(
                modifier = Modifier.padding(OrganicSpacing.lg)
            ) {
                OrganicPrimaryButton(
                    text = if (totalSelectedCount > 0) {
                        "Add $totalSelectedCount Task${if (totalSelectedCount == 1) "" else "s"} & Continue"
                    } else {
                        stringResource(Res.string.onboarding_tasks_skip)
                    },
                    onClick = {
                        if (selectedBrowseIds.isEmpty() && selectedSuggestionIds.isEmpty()) {
                            skipOnboarding("user_skip")
                        } else {
                            val residence = dataManager.residences.value.firstOrNull()
                            if (residence != null) {
                                val today = DateUtils.getTodayString()
                                val taskRequests = mutableListOf<TaskCreateRequest>()

                                // Browse tab selections (backend-sourced templates).
                                allBrowseTasks
                                    .filter { it.id in selectedBrowseIds }
                                    .forEach { template ->
                                        taskRequests.add(
                                            TaskCreateRequest(
                                                residenceId = residence.id,
                                                title = template.title,
                                                description = template.description.takeIf { it.isNotBlank() },
                                                categoryId = template.categoryId,
                                                priorityId = null,
                                                inProgress = false,
                                                frequencyId = template.frequencyId,
                                                assignedToId = null,
                                                dueDate = today,
                                                estimatedCost = null,
                                                contractorId = null,
                                                templateId = template.id
                                            )
                                        )
                                    }

                                // For You tab selections (scored suggestions).
                                val suggestions = (suggestionsState as? ApiResult.Success<TaskSuggestionsResponse>)?.data?.suggestions
                                suggestions?.filter { it.template.id in selectedSuggestionIds }?.forEach { suggestion ->
                                    val tmpl = suggestion.template
                                    taskRequests.add(
                                        TaskCreateRequest(
                                            residenceId = residence.id,
                                            title = tmpl.title,
                                            description = tmpl.description.takeIf { it.isNotBlank() },
                                            categoryId = tmpl.categoryId,
                                            priorityId = null,
                                            inProgress = false,
                                            frequencyId = tmpl.frequencyId,
                                            assignedToId = null,
                                            dueDate = today,
                                            estimatedCost = null,
                                            contractorId = null,
                                            templateId = tmpl.id
                                        )
                                    )
                                }

                                viewModel.createTasks(residence.id, taskRequests)
                            } else {
                                onTasksAdded()
                            }
                        }
                    },
                    modifier = Modifier.fillMaxWidth(),
                    enabled = !isCreatingTasks,
                    isLoading = isCreatingTasks,
                    icon = Icons.AutoMirrored.Filled.ArrowForward
                )
            }
        }
    }
}

// ==================== For You Tab ====================

@Composable
private fun ForYouTabContent(
    suggestionsState: ApiResult<TaskSuggestionsResponse>,
    selectedSuggestionIds: Set<Int>,
    hasBrowseFallback: Boolean,
    onToggleSuggestion: (Int, Double) -> Unit,
    onRetry: () -> Unit,
    onSkip: () -> Unit,
    onSwitchToBrowse: () -> Unit,
    modifier: Modifier = Modifier
) {
    when (suggestionsState) {
        is ApiResult.Loading, ApiResult.Idle -> {
            LoadingPane(
                message = "Finding tasks for your home...",
                modifier = modifier
            )
        }
        is ApiResult.Success -> {
            val suggestions = suggestionsState.data.suggestions
            if (suggestions.isEmpty()) {
                EmptyPane(
                    message = "No personalised suggestions yet — browse the full catalog or skip this step.",
                    primaryLabel = if (hasBrowseFallback) "Browse All" else "Skip",
                    onPrimary = if (hasBrowseFallback) onSwitchToBrowse else onSkip,
                    modifier = modifier
                )
            } else {
                LazyColumn(
                    modifier = modifier.fillMaxWidth(),
                    contentPadding = PaddingValues(horizontal = OrganicSpacing.lg, vertical = OrganicSpacing.md)
                ) {
                    items(suggestions) { suggestion ->
                        SuggestionRow(
                            suggestion = suggestion,
                            isSelected = suggestion.template.id in selectedSuggestionIds,
                            onToggle = {
                                onToggleSuggestion(suggestion.template.id, suggestion.relevanceScore)
                            }
                        )
                        Spacer(modifier = Modifier.height(OrganicSpacing.sm))
                    }
                    item { Spacer(modifier = Modifier.height(AppSpacing.xl)) }
                }
            }
        }
        is ApiResult.Error -> {
            ErrorPane(
                headline = "Couldn't load your suggestions",
                body = suggestionsState.message.takeIf { it.isNotBlank() }
                    ?: "Check your connection and try again.",
                onRetry = onRetry,
                onSkip = onSkip,
                modifier = modifier
            )
        }
    }
}

@Composable
private fun SuggestionRow(
    suggestion: TaskSuggestionResponse,
    isSelected: Boolean,
    onToggle: () -> Unit
) {
    val template = suggestion.template
    val relevancePercent = (suggestion.relevanceScore * 100).toInt()

    OrganicCard(
        modifier = Modifier
            .fillMaxWidth()
            .clickable { onToggle() },
        accentColor = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant,
        showBlob = false
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(OrganicSpacing.md),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Box(
                modifier = Modifier
                    .size(28.dp)
                    .clip(CircleShape)
                    .background(
                        if (isSelected) MaterialTheme.colorScheme.primary
                        else MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
                    ),
                contentAlignment = Alignment.Center
            ) {
                if (isSelected) {
                    Icon(Icons.Default.Check, contentDescription = "Selected", tint = Color.White, modifier = Modifier.size(16.dp) )
                }
            }

            Spacer(modifier = Modifier.width(OrganicSpacing.md))

            Column(modifier = Modifier.weight(1f)) {
                Text(
                    text = template.title,
                    style = MaterialTheme.typography.bodyMedium,
                    fontWeight = FontWeight.Medium,
                    color = MaterialTheme.colorScheme.onSurface
                )
                Text(
                    text = template.frequencyDisplay,
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant
                )
                if (suggestion.matchReasons.isNotEmpty()) {
                    Text(
                        text = suggestion.matchReasons.first(),
                        style = MaterialTheme.typography.labelSmall,
                        color = MaterialTheme.colorScheme.primary.copy(alpha = 0.8f)
                    )
                }
            }

            Surface(
                shape = RoundedCornerShape(OrganicRadius.lg),
                color = MaterialTheme.colorScheme.primary.copy(
                    alpha = (suggestion.relevanceScore * 0.2f).toFloat().coerceIn(0.05f, 0.2f)
                )
            ) {
                Text(
                    text = "$relevancePercent%",
                    modifier = Modifier.padding(horizontal = OrganicSpacing.sm, vertical = OrganicSpacing.xs),
                    style = MaterialTheme.typography.labelSmall,
                    fontWeight = FontWeight.Bold,
                    color = MaterialTheme.colorScheme.primary
                )
            }
        }
    }
}

// ==================== Browse Tab ====================

@Composable
private fun BrowseTabContent(
    templatesGroupedState: ApiResult<TaskTemplatesGroupedResponse>,
    browseCategories: List<OnboardingTaskCategory>,
    selectedTaskIds: Set<Int>,
    expandedCategoryId: Int?,
    onToggleExpand: (Int) -> Unit,
    onToggleTask: (OnboardingTaskTemplate) -> Unit,
    onRetry: () -> Unit,
    onSkip: () -> Unit,
    modifier: Modifier = Modifier
) {
    when (templatesGroupedState) {
        is ApiResult.Loading, ApiResult.Idle -> {
            LoadingPane(message = "Loading the task catalog...", modifier = modifier)
        }
        is ApiResult.Error -> {
            ErrorPane(
                headline = "Couldn't load the task catalog",
                body = templatesGroupedState.message.takeIf { it.isNotBlank() }
                    ?: "Check your connection and try again.",
                onRetry = onRetry,
                onSkip = onSkip,
                modifier = modifier
            )
        }
        is ApiResult.Success -> {
            if (browseCategories.isEmpty()) {
                EmptyPane(
                    message = "No templates available right now.",
                    primaryLabel = "Skip",
                    onPrimary = onSkip,
                    modifier = modifier
                )
            } else {
                LazyColumn(
                    modifier = modifier.fillMaxWidth(),
                    contentPadding = PaddingValues(horizontal = OrganicSpacing.lg, vertical = OrganicSpacing.md)
                ) {
                    items(browseCategories) { category ->
                        TaskCategorySection(
                            category = category,
                            selectedTaskIds = selectedTaskIds,
                            isExpanded = expandedCategoryId == category.id,
                            onToggleExpand = { onToggleExpand(category.id) },
                            onToggleTask = { template -> onToggleTask(template) }
                        )
                        Spacer(modifier = Modifier.height(OrganicSpacing.md))
                    }
                    item { Spacer(modifier = Modifier.height(AppSpacing.xl)) }
                }
            }
        }
    }
}

// ==================== Category / Row Components ====================

@Composable
private fun TaskCategorySection(
    category: OnboardingTaskCategory,
    selectedTaskIds: Set<Int>,
    isExpanded: Boolean,
    onToggleExpand: () -> Unit,
    onToggleTask: (OnboardingTaskTemplate) -> Unit
) {
    val selectedInCategory = category.tasks.count { it.id in selectedTaskIds }

    OrganicCard(
        modifier = Modifier.fillMaxWidth(),
        accentColor = category.color,
        showBlob = false
    ) {
        Column(modifier = Modifier.fillMaxWidth()) {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .clickable { onToggleExpand() }
                    .padding(OrganicSpacing.md),
                verticalAlignment = Alignment.CenterVertically
            ) {
                OrganicIconContainer(
                    icon = category.icon,
                    size = 44.dp,
                    iconSize = 24.dp,
                    gradientColors = listOf(category.color),
                    contentDescription = null
                )

                Spacer(modifier = Modifier.width(OrganicSpacing.md))

                Text(
                    text = category.name,
                    style = MaterialTheme.typography.titleMedium,
                    fontWeight = FontWeight.SemiBold,
                    modifier = Modifier.weight(1f)
                )

                if (selectedInCategory > 0) {
                    Box(
                        modifier = Modifier
                            .size(24.dp)
                            .clip(CircleShape)
                            .background(category.color),
                        contentAlignment = Alignment.Center
                    ) {
                        Text(
                            text = selectedInCategory.toString(),
                            style = MaterialTheme.typography.labelSmall,
                            fontWeight = FontWeight.Bold,
                            color = Color.White
                        )
                    }
                    Spacer(modifier = Modifier.width(OrganicSpacing.sm))
                }

                Icon(
                    imageVector = if (isExpanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
                    contentDescription = if (isExpanded) "Collapse" else "Expand",
                    tint = MaterialTheme.colorScheme.onSurfaceVariant
                )
            }

            AnimatedVisibility(
                visible = isExpanded,
                enter = fadeIn() + expandVertically(),
                exit = fadeOut() + shrinkVertically()
            ) {
                Column(modifier = Modifier.fillMaxWidth()) {
                    category.tasks.forEachIndexed { index, task ->
                        val isSelected = task.id in selectedTaskIds

                        TaskTemplateRow(
                            task = task,
                            isSelected = isSelected,
                            categoryColor = category.color,
                            onClick = { onToggleTask(task) }
                        )

                        if (index < category.tasks.lastIndex) {
                            OrganicDivider(
                                modifier = Modifier.padding(start = 60.dp),
                                color = MaterialTheme.colorScheme.outline.copy(alpha = 0.2f)
                            )
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun TaskTemplateRow(
    task: OnboardingTaskTemplate,
    isSelected: Boolean,
    categoryColor: Color,
    onClick: () -> Unit
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clickable { onClick() }
            .padding(horizontal = OrganicSpacing.md, vertical = OrganicSpacing.sm),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Box(
            modifier = Modifier
                .size(28.dp)
                .clip(CircleShape)
                .background(
                    if (isSelected) categoryColor
                    else MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
                ),
            contentAlignment = Alignment.Center
        ) {
            if (isSelected) {
                Icon(Icons.Default.Check, contentDescription = "Selected", tint = Color.White, modifier = Modifier.size(16.dp) )
            }
        }

        Spacer(modifier = Modifier.width(OrganicSpacing.md))

        Column(modifier = Modifier.weight(1f)) {
            Text(
                text = task.title,
                style = MaterialTheme.typography.bodyMedium,
                fontWeight = FontWeight.Medium,
                color = MaterialTheme.colorScheme.onSurface
            )
            Text(
                text = task.frequencyLabel,
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        }

        Icon(
            imageVector = task.icon,
            contentDescription = null, // decorative
            modifier = Modifier.size(24.dp),
            tint = categoryColor.copy(alpha = 0.6f)
        )
    }
}

// ==================== Shared state panes ====================

@Composable
private fun LoadingPane(message: String, modifier: Modifier = Modifier) {
    Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            CircularProgressIndicator(color = MaterialTheme.colorScheme.primary)
            Spacer(modifier = Modifier.height(OrganicSpacing.md))
            Text(
                text = message,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        }
    }
}

@Composable
private fun ErrorPane(
    headline: String,
    body: String,
    onRetry: () -> Unit,
    onSkip: () -> Unit,
    modifier: Modifier = Modifier
) {
    Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
        Column(
            modifier = Modifier.padding(OrganicSpacing.lg),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Icon(
                imageVector = Icons.Default.CloudOff,
                contentDescription = "Offline",
                modifier = Modifier.size(48.dp),
                tint = MaterialTheme.colorScheme.error
            )
            Spacer(modifier = Modifier.height(OrganicSpacing.md))
            Text(
                text = headline,
                style = MaterialTheme.typography.titleMedium,
                fontWeight = FontWeight.SemiBold,
                color = MaterialTheme.colorScheme.onSurface,
                textAlign = TextAlign.Center
            )
            Spacer(modifier = Modifier.height(OrganicSpacing.sm))
            Text(
                text = body,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                textAlign = TextAlign.Center
            )
            Spacer(modifier = Modifier.height(OrganicSpacing.lg))
            Row(horizontalArrangement = Arrangement.spacedBy(OrganicSpacing.md)) {
                OutlinedButton(onClick = onSkip) {
                    Text("Skip for now")
                }
                OutlinedButton(onClick = onRetry) {
                    Icon(Icons.Default.Refresh, contentDescription = null, modifier = Modifier.size(18.dp) ) // decorative
                    Spacer(modifier = Modifier.width(OrganicSpacing.xs))
                    Text("Retry")
                }
            }
        }
    }
}

@Composable
private fun EmptyPane(
    message: String,
    primaryLabel: String,
    onPrimary: () -> Unit,
    modifier: Modifier = Modifier
) {
    Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
        Column(
            modifier = Modifier.padding(OrganicSpacing.lg),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Icon(
                imageVector = Icons.Default.Inbox,
                contentDescription = null, // decorative
                modifier = Modifier.size(48.dp),
                tint = MaterialTheme.colorScheme.onSurfaceVariant
            )
            Spacer(modifier = Modifier.height(OrganicSpacing.md))
            Text(
                text = message,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                textAlign = TextAlign.Center
            )
            Spacer(modifier = Modifier.height(OrganicSpacing.lg))
            Button(onClick = onPrimary) { Text(primaryLabel) }
        }
    }
}

// ==================== Mapping: server → adapter ====================

private fun TaskTemplateCategoryGroup.toAdapter(): OnboardingTaskCategory {
    val categoryName = categoryName.ifBlank { "Uncategorized" }
    return OnboardingTaskCategory(
        id = categoryId ?: stableFallbackIdFor(categoryName),
        name = categoryName,
        icon = iconForCategory(categoryName),
        color = colorForCategory(categoryName),
        tasks = templates.map { it.toAdapter(categoryName) }
    )
}

private fun TaskTemplate.toAdapter(categoryName: String): OnboardingTaskTemplate {
    val label = frequency?.displayName ?: frequency?.name?.replaceFirstChar { it.uppercase() } ?: "One time"
    return OnboardingTaskTemplate(
        id = id,
        title = title,
        description = description,
        categoryId = categoryId,
        frequencyId = frequencyId,
        frequencyLabel = label,
        icon = iconForCategory(categoryName)
    )
}

// Stable but deterministic negative IDs for the "Uncategorized" bucket and
// any other server category that arrives without an ID. We never send these
// back to the server, they only drive UI keys + expanded-state tracking.
private fun stableFallbackIdFor(name: String): Int = -(name.hashCode().let { if (it == Int.MIN_VALUE) 0 else kotlin.math.abs(it) })

private fun iconForCategory(name: String): ImageVector {
    val n = name.lowercase()
    return when {
        "hvac" in n || "climate" in n -> Icons.Default.Thermostat
        "safety" in n || "security" in n -> Icons.Default.Security
        "plumb" in n || "water" in n -> Icons.Default.Water
        "outdoor" in n || "yard" in n || "landscap" in n -> Icons.Default.Park
        "appliance" in n || "kitchen" in n -> Icons.Default.Kitchen
        "exterior" in n || "roof" in n -> Icons.Default.Roofing
        "interior" in n -> Icons.Default.Weekend
        "electric" in n -> Icons.Default.Bolt
        "clean" in n -> Icons.Default.CleaningServices
        else -> Icons.Default.Home
    }
}

private fun colorForCategory(name: String): Color {
    // Deterministic rotation across five palette accents so each category
    // renders a distinct card border. Cheap hash-mod mapping; stable within
    // a session for a given category name.
    val palette = listOf(
        Color(0xFF07A0C3), // primary blue-green
        Color(0xFFF5A623), // amber
        Color(0xFF34C759), // green
        Color(0xFFAF52DE), // purple
        Color(0xFF0055A5)  // cerulean
    )
    val idx = kotlin.math.abs(name.hashCode()) % palette.size
    return palette[idx]
}
