package com.tt.honeyDue.widget

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

/**
 * Legacy DataStore instance used by the existing Android widgets prior to
 * iOS parity. Retained so currently-shipped widgets continue to compile
 * while Streams K/L/M roll out.
 */
private val Context.widgetDataStore: DataStore<Preferences> by preferencesDataStore(name = "widget_data")

/**
 * Legacy widget task model (pre-iOS-parity). Prefer [WidgetTaskDto] for new
 * code — this type remains only to keep the current widget UI compiling.
 */
@Serializable
data class WidgetTask(
    val id: Int,
    val title: String,
    val residenceName: String,
    val dueDate: String?,
    val isOverdue: Boolean,
    val categoryName: String,
    val priorityLevel: Int
)

/**
 * Legacy summary model (pre-iOS-parity). Prefer [WidgetStats] + [WidgetTaskDto]
 * via the iOS-parity API below.
 */
@Serializable
data class WidgetSummary(
    val overdueCount: Int = 0,
    val dueSoonCount: Int = 0,
    val inProgressCount: Int = 0,
    val totalTasksCount: Int = 0,
    val tasks: List<WidgetTask> = emptyList(),
    val lastUpdated: Long = System.currentTimeMillis()
)

/**
 * Repository for widget data persistence.
 *
 * This class exposes two APIs:
 *
 *  1. **iOS-parity API** (preferred):
 *     [saveTasks], [loadTasks], [markPendingCompletion],
 *     [clearPendingCompletion], [computeStats], [saveTierState],
 *     [loadTierState]. Mirrors the semantics of
 *     `iosApp/iosApp/Helpers/WidgetDataManager.swift`. Backed by
 *     [WidgetDataStore].
 *
 *  2. **Legacy API** (retained for current widgets):
 *     [widgetSummary], [isProUser], [userName], [updateWidgetData],
 *     [updateProStatus], [updateUserName], [clearData]. These will be
 *     removed once Streams K/L/M land.
 *
 * Singleton accessors: [get] (new) and [getInstance] (legacy) return the
 * same underlying instance.
 */
class WidgetDataRepository internal constructor(private val context: Context) {

    private val json = Json {
        ignoreUnknownKeys = true
        encodeDefaults = true
    }

    /** iOS-parity DataStore wrapper. */
    private val store = WidgetDataStore(context)

    // =====================================================================
    // iOS-parity API
    // =====================================================================

    /**
     * Serialize and persist the task list to the widget cache. Overwrites any
     * previous list (matches iOS file-write semantics — the JSON blob is the
     * entire cache, not an append).
     */
    suspend fun saveTasks(tasks: List<WidgetTaskDto>) {
        val encoded = json.encodeToString(tasks)
        store.writeTasksJson(encoded, refreshTimeMs = System.currentTimeMillis())
    }

    /**
     * Load the cached task list, excluding any ids present in the
     * pending-completion set.
     *
     * iOS semantics: the widget's `loadTasks()` returns whatever is on disk;
     * pending completions are filtered by a separate `PendingTaskState` file.
     * Here we fold that filter into [loadTasks] so callers don't have to
     * remember to apply it.
     */
    suspend fun loadTasks(): List<WidgetTaskDto> {
        val raw = store.readTasksJson()
        val all = try {
            json.decodeFromString<List<WidgetTaskDto>>(raw)
        } catch (e: Exception) {
            emptyList()
        }
        val pending = store.readPendingCompletionIds()
        if (pending.isEmpty()) return all
        return all.filterNot { it.id in pending }
    }

    /** Queue a task id for optimistic completion. See [loadTasks]. */
    suspend fun markPendingCompletion(taskId: Long) {
        val current = store.readPendingCompletionIds().toMutableSet()
        current.add(taskId)
        store.writePendingCompletionIds(current)
    }

    /** Remove a task id from the pending-completion set. */
    suspend fun clearPendingCompletion(taskId: Long) {
        val current = store.readPendingCompletionIds().toMutableSet()
        current.remove(taskId)
        store.writePendingCompletionIds(current)
    }

    /** Whether a task id is currently queued for optimistic completion. */
    suspend fun isPendingCompletion(taskId: Long): Boolean =
        taskId in store.readPendingCompletionIds()

    /**
     * Compute the three summary counters shown on the widget:
     *  - overdueCount    — tasks with `isOverdue == true`
     *  - dueWithin7      — tasks with `0 <= daysUntilDue <= 7`
     *  - dueWithin8To30  — tasks with `8 <= daysUntilDue <= 30`
     *
     * Pending-completion tasks are excluded (via [loadTasks]).
     */
    suspend fun computeStats(): WidgetStats {
        val tasks = loadTasks()
        var overdue = 0
        var within7 = 0
        var within8To30 = 0
        for (t in tasks) {
            if (t.isOverdue) overdue += 1
            val d = t.daysUntilDue
            when {
                d in 0..7 -> within7 += 1
                d in 8..30 -> within8To30 += 1
            }
        }
        return WidgetStats(
            overdueCount = overdue,
            dueWithin7 = within7,
            dueWithin8To30 = within8To30
        )
    }

    /** Persist the subscription tier ("free" | "premium"). */
    suspend fun saveTierState(tier: String) {
        store.writeTier(tier)
    }

    /** Read the persisted tier. Defaults to "free" if never set. */
    suspend fun loadTierState(): String = store.readTier()

    /** Clear every key in both the iOS-parity store and the legacy store. */
    internal suspend fun clearAll() {
        store.clearAll()
        context.widgetDataStore.edit { it.clear() }
    }

    // =====================================================================
    // Legacy API (kept until Streams K/L/M replace the widget UI)
    // =====================================================================

    val widgetSummary: Flow<WidgetSummary> = context.widgetDataStore.data.map { preferences ->
        val tasksJson = preferences[TASKS_JSON] ?: "[]"
        val tasks = try {
            json.decodeFromString<List<WidgetTask>>(tasksJson)
        } catch (e: Exception) {
            emptyList()
        }

        WidgetSummary(
            overdueCount = preferences[OVERDUE_COUNT] ?: 0,
            dueSoonCount = preferences[DUE_SOON_COUNT] ?: 0,
            inProgressCount = preferences[IN_PROGRESS_COUNT] ?: 0,
            totalTasksCount = preferences[TOTAL_TASKS_COUNT] ?: 0,
            tasks = tasks,
            lastUpdated = preferences[LAST_UPDATED] ?: 0L
        )
    }

    val isProUser: Flow<Boolean> = context.widgetDataStore.data.map { preferences ->
        preferences[IS_PRO_USER] == "true"
    }

    val userName: Flow<String> = context.widgetDataStore.data.map { preferences ->
        preferences[USER_NAME] ?: ""
    }

    suspend fun updateWidgetData(summary: WidgetSummary) {
        context.widgetDataStore.edit { preferences ->
            preferences[OVERDUE_COUNT] = summary.overdueCount
            preferences[DUE_SOON_COUNT] = summary.dueSoonCount
            preferences[IN_PROGRESS_COUNT] = summary.inProgressCount
            preferences[TOTAL_TASKS_COUNT] = summary.totalTasksCount
            preferences[TASKS_JSON] = json.encodeToString(summary.tasks)
            preferences[LAST_UPDATED] = System.currentTimeMillis()
        }
    }

    suspend fun updateProStatus(isPro: Boolean) {
        context.widgetDataStore.edit { preferences ->
            preferences[IS_PRO_USER] = if (isPro) "true" else "false"
        }
    }

    suspend fun updateUserName(name: String) {
        context.widgetDataStore.edit { preferences ->
            preferences[USER_NAME] = name
        }
    }

    suspend fun clearData() {
        context.widgetDataStore.edit { preferences ->
            preferences.clear()
        }
    }

    companion object {
        // Legacy keys — preserved for on-disk compatibility.
        private val OVERDUE_COUNT = intPreferencesKey("overdue_count")
        private val DUE_SOON_COUNT = intPreferencesKey("due_soon_count")
        private val IN_PROGRESS_COUNT = intPreferencesKey("in_progress_count")
        private val TOTAL_TASKS_COUNT = intPreferencesKey("total_tasks_count")
        private val TASKS_JSON = stringPreferencesKey("tasks_json")
        private val LAST_UPDATED = longPreferencesKey("last_updated")
        private val IS_PRO_USER = stringPreferencesKey("is_pro_user")
        private val USER_NAME = stringPreferencesKey("user_name")

        @Volatile
        private var INSTANCE: WidgetDataRepository? = null

        /** Preferred accessor — matches iOS `WidgetDataManager.shared`. */
        fun get(context: Context): WidgetDataRepository {
            return INSTANCE ?: synchronized(this) {
                INSTANCE ?: WidgetDataRepository(context.applicationContext).also { INSTANCE = it }
            }
        }

        /** Legacy accessor — delegates to [get]. */
        fun getInstance(context: Context): WidgetDataRepository = get(context)
    }
}
