package com.tt.honeyDue.widget

import android.content.Context
import android.content.Intent
import android.net.Uri
import com.tt.honeyDue.models.TaskCompletionCreateRequest
import com.tt.honeyDue.network.APILayer
import com.tt.honeyDue.network.ApiResult

/**
 * Coordinates the server-side effect of a "complete task" tap on a widget.
 *
 * Mirrors `iosApp/iosApp/Helpers/WidgetActionProcessor.swift` semantics:
 *
 *  - **Free tier:** do not hit the API. Fire an `ACTION_VIEW` intent against
 *    the `honeydue://paywall?from=widget` deep link so the hosting app can
 *    land on the upgrade flow. Return [Result.FreeTier].
 *  - **Premium:** record optimistic pending-completion state in
 *    [WidgetDataRepository] (which hides the task from subsequent renders),
 *    call [APILayer.createTaskCompletion], then either
 *      (a) **success** — clear pending and ask [WidgetUpdateManager] to
 *          refresh so the now-completed task is confirmed gone, or
 *      (b) **failure** — roll back pending so the task reappears in the
 *          widget and the user can retry. Return [Result.Failed] carrying
 *          the error so the caller can surface a Toast / log / retry.
 *  - **Idempotent:** if the task is already in the pending set, return
 *    [Result.AlreadyPending] without hitting the API. A double-tap while
 *    the first completion is in flight must not double-complete.
 *
 * Test hooks ([refreshTrigger], [processOverrideForTest]) are intentionally
 * public-internal so Robolectric unit tests can observe side effects and
 * swap the entry point without reflection. Call [resetTestHooks] in test
 * teardown.
 */
object WidgetActionProcessor {

    sealed class Result {
        /** API completion succeeded. Task is gone from the widget's view. */
        object Success : Result()

        /** User is on the free tier. Paywall deep-link was fired instead. */
        object FreeTier : Result()

        /** Task is already in the pending set — duplicate tap, no-op. */
        object AlreadyPending : Result()

        /** API call failed. Pending state has been rolled back. */
        data class Failed(val error: Throwable) : Result()
    }

    /**
     * Entry point. Usually invoked from [CompleteTaskAction.onAction], which
     * runs on Glance's callback dispatcher. Safe to invoke off the main
     * thread.
     */
    suspend fun processComplete(context: Context, taskId: Long): Result {
        processOverrideForTest?.let { return it(context, taskId) }

        val repo = WidgetDataRepository.get(context)
        val store = WidgetDataStore(context)

        // Idempotent short-circuit: another tap is already in flight.
        if (store.readPendingCompletionIds().contains(taskId)) {
            return Result.AlreadyPending
        }

        // Tier gate — free users get the paywall, not the API.
        val tier = repo.loadTierState()
        if (tier != TIER_PREMIUM) {
            launchPaywall(context)
            return Result.FreeTier
        }

        // Optimistic UI: hide the task from the widget before hitting the API.
        repo.markPendingCompletion(taskId)

        val request = TaskCompletionCreateRequest(
            taskId = taskId.toInt(),
            notes = WIDGET_COMPLETION_NOTE
        )

        val apiResult: ApiResult<*> = try {
            APILayer.createTaskCompletion(request)
        } catch (t: Throwable) {
            repo.clearPendingCompletion(taskId)
            return Result.Failed(t)
        }

        return when (apiResult) {
            is ApiResult.Success<*> -> {
                // Completion synced. Clear the pending marker and force a
                // refresh so the worker re-fetches the (now shorter) task
                // list from the API.
                repo.clearPendingCompletion(taskId)
                refreshTrigger(context)
                Result.Success
            }
            is ApiResult.Error -> {
                // Server rejected the completion. Roll back the optimistic
                // state so the task re-appears in the widget.
                repo.clearPendingCompletion(taskId)
                val message = apiResult.message.ifBlank { "widget-complete failed" }
                Result.Failed(RuntimeException(message))
            }
            else -> {
                // ApiResult.Idle / ApiResult.Loading are not valid terminal
                // states here — treat as failure, roll back.
                repo.clearPendingCompletion(taskId)
                Result.Failed(
                    IllegalStateException("Unexpected terminal ApiResult: $apiResult")
                )
            }
        }
    }

    /** Fire the `honeydue://paywall?from=widget` deep link. */
    private fun launchPaywall(context: Context) {
        val uri = Uri.parse(PAYWALL_URI)
        val intent = Intent(Intent.ACTION_VIEW, uri).apply {
            // Widget callbacks don't run inside an Activity — must ask the
            // OS for a fresh task.
            flags = Intent.FLAG_ACTIVITY_NEW_TASK
            setPackage(context.packageName)
        }
        context.startActivity(intent)
    }

    // ====================================================================
    // Test hooks — see class kdoc. Do NOT reference from production code.
    // ====================================================================

    /**
     * Function called after a successful API completion to nudge the widget
     * host into re-rendering. Defaults to [WidgetUpdateManager.forceRefresh];
     * tests swap this to observe the call without mocking WorkManager.
     */
    @JvmField
    internal var refreshTrigger: (Context) -> Unit = { WidgetUpdateManager.forceRefresh(it) }

    /**
     * If set, [processComplete] short-circuits to this lambda instead of
     * running the real pipeline. Used by [CompleteTaskActionTest] to isolate
     * parameter-parsing behaviour from the processor's side effects.
     */
    @JvmField
    internal var processOverrideForTest: (suspend (Context, Long) -> Result)? = null

    /** Restore test hooks to production defaults. Call from `@After`. */
    internal fun resetTestHooks() {
        refreshTrigger = { WidgetUpdateManager.forceRefresh(it) }
        processOverrideForTest = null
    }

    private const val TIER_PREMIUM = "premium"
    private const val PAYWALL_URI = "honeydue://paywall?from=widget"
    private const val WIDGET_COMPLETION_NOTE = "Completed from widget"
}
