package com.anypreta.bountyboard.ui.task

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.anypreta.bountyboard.data.AppContainer
import com.anypreta.bountyboard.data.model.Task
import com.anypreta.bountyboard.data.net.ApproveClaimBody
import com.anypreta.bountyboard.data.net.AssignAiBody
import com.anypreta.bountyboard.data.net.AssignAiContext
import com.anypreta.bountyboard.data.net.ChecklistItem
import com.anypreta.bountyboard.data.net.ClaimBody
import com.anypreta.bountyboard.data.net.CommentBody
import com.anypreta.bountyboard.data.net.DeclineClaimBody
import com.anypreta.bountyboard.data.net.ExtendBody
import com.anypreta.bountyboard.data.net.IdsBody
import com.anypreta.bountyboard.data.net.ReviewBody
import com.anypreta.bountyboard.data.net.SubdivideBody
import com.anypreta.bountyboard.data.net.SubdivideConstraints
import com.anypreta.bountyboard.data.net.TaskPatchBody
import com.anypreta.bountyboard.data.net.TimeBody
import kotlinx.coroutines.launch

class TaskDetailViewModel(
    private val container: AppContainer,
    private val taskId: String,
) : ViewModel() {

    var loading by mutableStateOf(true)
        private set
    var error by mutableStateOf<String?>(null)
        private set
    var task by mutableStateOf<Task?>(null)
        private set

    var actionLoading by mutableStateOf(false)
        private set
    var actionError by mutableStateOf<String?>(null)
        private set
    var actionInfo by mutableStateOf<String?>(null)
        private set

    var names by mutableStateOf<Map<String, String>>(emptyMap())
        private set

    val myId: String? get() = container.session.user.value?.id

    init {
        load()
    }

    fun load() {
        loading = true; error = null
        viewModelScope.launch {
            try {
                val r = container.repo.call { task(taskId) }
                task = r.task
                resolveNames(r.task)
            } catch (e: Exception) {
                error = e.message
            } finally {
                loading = false
            }
        }
    }

    private fun resolveNames(t: Task) {
        val ids = buildSet {
            t.claimRequests?.forEach { add(it.developerId) }
            t.comments?.forEach { add(it.authorId) }
            t.assignee?.userId?.let { add(it) }
            t.consultantId.takeIf { it.isNotBlank() }?.let { add(it) }
        }.filter { it.isNotBlank() && it !in names }
        if (ids.isEmpty()) return
        viewModelScope.launch {
            val resolved = names.toMutableMap()
            ids.forEach { id ->
                try {
                    val c = container.repo.call { userCard(id) }
                    resolved[id] = c.card.name
                } catch (_: Exception) {
                }
            }
            names = resolved
        }
    }

    fun nameFor(id: String?): String = id?.let { names[it] ?: it.takeLast(6) } ?: "—"

    fun avatarFor(id: String?): String? = null // cards aren't cached for avatars; initials used

    private fun act(block: suspend () -> Any?) {
        actionLoading = true; actionError = null; actionInfo = null
        viewModelScope.launch {
            try {
                block()
                load()
            } catch (e: Exception) {
                actionError = e.message
            } finally {
                actionLoading = false
            }
        }
    }

    // Developer actions
    fun claim(note: String?) = act { container.repo.action { claim(taskId, ClaimBody(note)) } }
    fun withdrawClaim() = act { container.repo.action { withdrawClaim(taskId) } }
    fun start() = act { container.repo.action { start(taskId) } }
    fun submitReview() = act { container.repo.action { submitReview(taskId) } }
    fun abandon() = act { container.repo.action { abandon(taskId) } }
    fun comment(body: String) = act { container.repo.call { addComment(taskId, CommentBody(body)) } }
    fun logTime(minutes: Int, note: String?) = act { container.repo.call { logTime(taskId, TimeBody(minutes, note)) } }

    // Consultant actions
    fun approveClaim(devId: String) = act { container.repo.action { approveClaim(taskId, ApproveClaimBody(devId)) } }
    fun declineClaim(devId: String) = act { container.repo.action { declineClaim(taskId, DeclineClaimBody(devId)) } }
    fun unassign() = act { container.repo.action { unassign(taskId) } }
    fun publish() = act { container.repo.call { publish(taskId) } }
    fun archive() = act { container.repo.action { archiveTask(taskId) } }
    fun subdivide(note: String?, min: Int?, max: Int?) = act {
        val c = if (min != null || max != null) SubdivideConstraints(min, max) else null
        container.repo.call { subdivide(taskId, SubdivideBody(note?.ifBlank { null }, c, confirmReplace = true)) }
        actionInfo = "Subdivision queued — results stream in shortly."
    }
    fun extend(note: String) = act {
        container.repo.call { extend(taskId, ExtendBody(note)) }
        actionInfo = "Extension queued."
    }
    fun assignAi(repo: String?, branch: String?, instructions: String?) = act {
        val ctx = AssignAiContext(repo?.ifBlank { null }, branch?.ifBlank { null }, instructions?.ifBlank { null })
        container.repo.call { assignAi(taskId, AssignAiBody(ctx)) }
        actionInfo = "Work performer job created."
    }
    fun review(approve: Boolean, note: String?, checklist: List<ChecklistItem>) = act {
        container.repo.action {
            review(taskId, ReviewBody(if (approve) "approve" else "request_changes", note?.ifBlank { null }, checklist.ifEmpty { null }))
        }
    }
    fun patch(title: String, description: String, ac: List<String>, coefficient: Double, budget: Double, onDone: () -> Unit) {
        val v = task?.version ?: return
        actionLoading = true; actionError = null
        viewModelScope.launch {
            try {
                container.repo.call {
                    patchTask(taskId, TaskPatchBody(
                        title = title, description = description,
                        acceptanceCriteria = ac, effortCoefficient = coefficient, budget = budget, version = v,
                    ))
                }
                load()
                onDone()
            } catch (e: Exception) {
                actionError = e.message
            } finally {
                actionLoading = false
            }
        }
    }

    fun clearActionMsg() { actionError = null; actionInfo = null }
}
