package com.tt.honeyDue.ui.screens.task

import com.tt.honeyDue.models.TaskCreateRequest
import com.tt.honeyDue.models.TaskUpdateRequest
import kotlinx.datetime.LocalDate

/**
 * Immutable snapshot of the create/edit-task form field values.
 *
 * Mirrors the iOS `TaskFormState` container in
 * `iosApp/iosApp/Core/FormStates/TaskFormStates.swift` — kept purely
 * descriptive so it can be fed into pure validators.
 *
 * IDs are [Int] because [TaskCreateRequest]/[TaskUpdateRequest] use [Int].
 * The public plan spec asked for Long, but matching the existing wire
 * contract avoids a lossy conversion at the call-site.
 */
data class TaskFormFields(
    val title: String = "",
    val description: String = "",
    val priorityId: Int? = null,
    val categoryId: Int? = null,
    val frequencyId: Int? = null,
    val dueDate: LocalDate? = null,
    val estimatedCost: String = "",
    val residenceId: Int? = null,
)

/**
 * Per-field error snapshot — one nullable string per validated field. A
 * `null` entry means "no error".
 */
data class TaskFormErrors(
    val title: String? = null,
    val priorityId: String? = null,
    val categoryId: String? = null,
    val frequencyId: String? = null,
    val estimatedCost: String? = null,
    val residenceId: String? = null,
) {
    /** True when every entry is null — i.e. the form passes validation. */
    val isEmpty: Boolean
        get() = title == null &&
                priorityId == null &&
                categoryId == null &&
                frequencyId == null &&
                estimatedCost == null &&
                residenceId == null
}

/**
 * Mutable container that glues [TaskFormFields] to the pure validators in
 * [TaskFormValidation] and exposes typed request builders.
 *
 * Deliberately not a Compose `State` — keep this plain so common tests can
 * exercise it without a UI runtime. The Compose call-site can wrap the
 * container in a `remember { mutableStateOf(...) }` as needed.
 */
class TaskFormState(initial: TaskFormFields = TaskFormFields()) {

    var fields: TaskFormFields = initial
        private set

    /** Derived: recomputed from the current [fields] on every access. */
    val errors: TaskFormErrors
        get() = computeErrors(fields)

    /** Derived: `true` iff every validator returns `null`. */
    val isValid: Boolean
        get() = errors.isEmpty

    /** Replaces the current field snapshot. Errors/[isValid] update automatically. */
    fun update(f: TaskFormFields) {
        fields = f
    }

    /** Alias for `errors` — returned as an explicit snapshot for callers that prefer imperative style. */
    fun validate(): TaskFormErrors = errors

    /**
     * Builds a [TaskCreateRequest] from the current fields, or `null` if the
     * form does not validate. Blank description / empty cost are mapped to
     * `null` (matching iOS).
     */
    fun toCreateRequest(): TaskCreateRequest? {
        if (!isValid) return null
        val f = fields
        // Non-null asserts are safe: isValid implies all required fields are set.
        return TaskCreateRequest(
            residenceId = f.residenceId!!,
            title = f.title,
            description = f.description.ifBlank { null },
            categoryId = f.categoryId,
            priorityId = f.priorityId,
            frequencyId = f.frequencyId,
            dueDate = f.dueDate?.toString(),
            estimatedCost = f.estimatedCost.ifBlank { null }?.toDoubleOrNull(),
        )
    }

    /**
     * Builds a [TaskUpdateRequest] from the current fields. Unlike create,
     * edit mode does not require a residence (the task already has one),
     * so this returns `null` only when the non-residence validators fail.
     */
    fun toUpdateRequest(taskId: Int): TaskUpdateRequest? {
        val errs = errors
        val editErrorsPresent = errs.title != null ||
                errs.priorityId != null ||
                errs.categoryId != null ||
                errs.frequencyId != null ||
                errs.estimatedCost != null
        if (editErrorsPresent) return null
        val f = fields
        return TaskUpdateRequest(
            title = f.title,
            description = f.description.ifBlank { null },
            categoryId = f.categoryId,
            priorityId = f.priorityId,
            frequencyId = f.frequencyId,
            dueDate = f.dueDate?.toString(),
            estimatedCost = f.estimatedCost.ifBlank { null }?.toDoubleOrNull(),
        )
    }

    /**
     * Overload that accepts [Long] for symmetry with screens that pull the
     * task id from navigation arguments typed as Long. Narrows to [Int]
     * because the wire model expects Int.
     */
    fun toUpdateRequest(taskId: Long): TaskUpdateRequest? =
        toUpdateRequest(taskId.toInt())

    private fun computeErrors(f: TaskFormFields): TaskFormErrors = TaskFormErrors(
        title = TaskFormValidation.validateTitle(f.title),
        priorityId = TaskFormValidation.validatePriorityId(f.priorityId),
        categoryId = TaskFormValidation.validateCategoryId(f.categoryId),
        frequencyId = TaskFormValidation.validateFrequencyId(f.frequencyId),
        estimatedCost = TaskFormValidation.validateEstimatedCost(f.estimatedCost),
        residenceId = TaskFormValidation.validateResidenceId(f.residenceId),
    )
}
