<!-- #region TaskHistory [C:3] [TYPE Component] [SEMANTICS task, history, list, status, monitoring] -->
<!-- @BRIEF Component component: components/TaskHistory.svelte -->
<!-- @LAYER UI -->
<!--
@SEMANTICS: task, history, list, status, monitoring
@PURPOSE: Displays a list of recent tasks with their status and allows selecting them for viewing logs.
@LAYER UI
@RELATION USES -> [EXT:frontend:stores]
@RELATION USES -> [ApiModule]
-->
<script lang="ts">
    import { onMount, onDestroy } from 'svelte';
    import { selectedTask } from '$lib/stores.svelte.js';
    import { api, getTaskEventsWsUrl } from '../lib/api.js';
    import { t } from '$lib/i18n/index.svelte.js';
    
    /** @type {string | undefined} — if set, only shows tasks with this plugin_id (e.g. "superset-migration") */
    let { pluginId = undefined } = $props();
    
    let tasks = $state([]);
    let loading = $state(true);
    let error = $state("");
    let taskWs = null;
    
    // #region fetchTasks:Function [TYPE Function]
    // @PURPOSE: Fetches the list of recent tasks from the API (initial load).
    // @PRE: None.
    // @POST: tasks array is updated and selectedTask status synchronized.
    async function fetchTasks() {
        try {
            const filterParam = pluginId ? `&plugin_id=${encodeURIComponent(pluginId)}` : '';
            tasks = await api.fetchApi(`/tasks?limit=10${filterParam}`);

            // Update selected task if it exists in the list (for status updates)
            if ($selectedTask) {
                const updatedTask = tasks.find(t => t.id === $selectedTask.id);
                if (updatedTask && updatedTask.status !== $selectedTask.status) {
                    selectedTask.set(updatedTask);
                }
            }
        } catch (e) {
            error = e.message;
        } finally {
            loading = false;
        }
    }
    // #endregion fetchTasks:Function

    // #region connectTaskEventsWs:Function [TYPE Function]
    // @PURPOSE: Opens a WebSocket to /ws/task-events for real-time task status updates.
    // @POST: WebSocket is connected and handlers attached.
    function connectTaskEventsWs() {
        try {
            const url = getTaskEventsWsUrl();
            taskWs = new WebSocket(url);

            taskWs.onopen = () => {
                console.log("[TaskHistory][WS] Connected to task events");
            };

            taskWs.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    if (data.type === "task_status") {
                        const updatedTask = data.task;
                        const idx = tasks.findIndex(t => t.id === updatedTask.id);
                        if (idx >= 0) {
                            // Create new array to trigger $state reactivity
                            tasks = tasks.map((t, i) =>
                                i === idx ? { ...t, ...updatedTask } : t
                            );
                        } else if (!pluginId || updatedTask.plugin_id === pluginId) {
                            // New task — add to top, keep max 10
                            tasks = [updatedTask, ...tasks].slice(0, 10);
                        }
                    }
                } catch (_e) {
                    // Ignore parse errors
                }
            };

            taskWs.onerror = () => {
                console.warn("[TaskHistory][WS] Error, will retry");
                // Don't set taskWs to null — onclose will handle reconnect
            };

            taskWs.onclose = () => {
                console.log("[TaskHistory][WS] Disconnected");
                taskWs = null;
                // Reconnect after 10s
                setTimeout(() => {
                    if (taskWs) return; // already reconnected
                    connectTaskEventsWs();
                }, 10000);
            };
        } catch (_e) {
            taskWs = null;
        }
    }
    // #endregion connectTaskEventsWs:Function

    // #region clearTasks:Function [TYPE Function]
    // @PURPOSE: Clears tasks from the history, optionally filtered by status.
    // @PRE: User confirms deletion via prompt.
    // @POST: Tasks are deleted from backend and list is re-fetched.
    async function clearTasks(status = null) {
        if (!confirm($t.tasks?.clear_confirm )) return;
        try {
            let endpoint = '/tasks';
            if (status) endpoint += `?status=${status}`;
            
            await api.requestApi(endpoint, 'DELETE');
            await fetchTasks();
        } catch (e) {
            error = e.message;
        }
    }
    // #endregion clearTasks:Function

    // #region selectTask:Function [TYPE Function]
    // @PURPOSE: Selects a task and fetches its full details.
    // @PRE: task object is provided.
    // @POST: selectedTask store is updated with full task details.
    async function selectTask(task) {
        try {
            // Fetch the full task details (including logs) before setting it as selected
            const fullTask = await api.getTask(task.id);
            selectedTask.set(fullTask);
        } catch (e) {
            console.error($t.tasks?.fetch_task_details_failed , e);
            selectedTask.set(task);
        }
    }
    // #endregion selectTask:Function
    
    // #region getStatusColor:Function [TYPE Function]
    // @PURPOSE: Returns the CSS color class for a given task status.
    // @PRE: status string is provided.
    // @POST: Returns tailwind color class string.
    function getStatusColor(status) {
        const s = status?.toLowerCase();
        if (s === "success" || s === "completed") return "bg-green-100 text-green-800";
        if (s === "failed" || s === "error") return "bg-red-100 text-red-800";
        if (s === "running" || s === "in_progress") return "bg-blue-100 text-blue-800";
        if (s === "partial" || s === "partial_success") return "bg-amber-100 text-amber-800";
        return "bg-gray-100 text-gray-800";
    }
    // #endregion getStatusColor:Function
    
    // #region onMount:Function [TYPE Function]
    // @PURPOSE: Initializes the component by fetching tasks and starting WebSocket.
    // @PRE: Component is mounting.
    // @POST: Tasks are fetched via HTTP, WebSocket connects for real-time updates.
    onMount(() => {
        fetchTasks();
        connectTaskEventsWs();
    });
    // #endregion onMount:Function
    
    // #region onDestroy:Function [TYPE Function]
    // @PURPOSE: Closes the WebSocket when the component is destroyed.
    // @PRE: Component is being destroyed.
    // @POST: WebSocket is closed.
    onDestroy(() => {
        if (taskWs) {
            taskWs.onclose = null; // prevent reconnect
            taskWs.close();
            taskWs = null;
        }
    });
    // #endregion onDestroy:Function
</script>

<div class="bg-white shadow overflow-hidden sm:rounded-lg mb-8">
    <div class="px-4 py-5 sm:px-6 flex justify-between items-center">
        <h3 class="text-lg leading-6 font-medium text-gray-900">
            {$t.tasks?.recent }
        </h3>
        <div class="flex space-x-4 items-center">
            <div class="relative inline-block text-left group">
                <button class="text-sm text-red-600 hover:text-red-900 focus:outline-none flex items-center py-2">
                    {$t.tasks?.clear_tasks }
                    <svg class="ml-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg>
                </button>
                <!-- Added a transparent bridge to prevent menu closing when moving cursor -->
                <div class="absolute h-2 w-full top-full left-0"></div>
                <div class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none hidden group-hover:block z-50">
                    <div class="py-1">
                        <button onclick={() => clearTasks()} class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">{$t.tasks?.clear_non_running }</button>
                        <button onclick={() => clearTasks('FAILED')} class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">{$t.tasks?.clear_failed }</button>
                        <button onclick={() => clearTasks('AWAITING_INPUT')} class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">{$t.tasks?.clear_awaiting_input }</button>
                    </div>
                </div>
            </div>
            <button
                onclick={fetchTasks}
                class="text-sm text-indigo-600 hover:text-indigo-900 focus:outline-none"
            >
                {$t.common?.refresh }
            </button>
        </div>
    </div>
    
    {#if loading && tasks.length === 0}
        <div class="p-4 text-center text-gray-500">{$t.tasks?.loading }</div>
    {:else if error}
        <div class="p-4 text-center text-red-500">{error}</div>
    {:else if tasks.length === 0}
        <div class="p-4 text-center text-gray-500">{$t.tasks?.no_tasks }</div>
    {:else}
        <ul class="divide-y divide-gray-200">
            {#each tasks as task}
                <li>
                    <button 
                        class="w-full text-left block hover:bg-gray-50 focus:outline-none focus:bg-gray-50 transition duration-150 ease-in-out"
                        class:bg-indigo-50={$selectedTask && $selectedTask.id === task.id}
                        onclick={() => selectTask(task)}
                    >
                        <div class="px-4 py-4 sm:px-6">
                            <div class="flex items-center justify-between">
                                <p class="text-sm font-medium text-indigo-600 truncate">
                                    {task.plugin_id}
                                    <span class="text-gray-500 text-xs ml-2">({task.id.slice(0, 8)})</span>
                                </p>
                                <div class="ml-2 flex-shrink-0 flex">
                                    <p class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {getStatusColor(task.status)}">
                                        {task.status}
                                    </p>
                                </div>
                            </div>
                            <div class="mt-2 sm:flex sm:justify-between">
                                <div class="sm:flex">
                                    <p class="flex items-center text-sm text-gray-500">
                                        {#if task.params.from_env && task.params.to_env}
                                            {task.params.from_env} &rarr; {task.params.to_env}
                                        {:else}
                                            {$t.tasks?.parameters }: {Object.keys(task.params).length} {$t.tasks?.keys }
                                        {/if}
                                    </p>
                                </div>
                                <div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0">
                                    <p>
                                        {$t.tasks?.started_label }: {new Date(task.started_at || task.created_at || Date.now()).toLocaleString()}
                                    </p>
                                </div>
                            </div>
                        </div>
                    </button>
                </li>
            {/each}
        </ul>
    {/if}
</div>
<!-- #endregion TaskHistory -->
