<!-- #region DashboardDetail [C:4] [TYPE Page] [SEMANTICS sveltekit, dashboard, detail, git, llm] -->
<!-- @BRIEF Dashboard detail view showing metadata, thumbnail, linked charts/datasets, Git status, task history, and LLM validation. -->
<!-- @LAYER Page -->
<!-- @RELATION DEPENDS_ON -> [DashboardHeader] -->
<!-- @RELATION DEPENDS_ON -> [DashboardGitManager] -->
<!-- @RELATION DEPENDS_ON -> [DashboardLinkedResources] -->
<!-- @RELATION DEPENDS_ON -> [DashboardTaskHistory] -->
<!-- @RELATION DEPENDS_ON -> [GitManager] -->
<!-- @RELATION DEPENDS_ON -> [EXT:frontend:BranchSelector] -->
<!-- @RELATION DEPENDS_ON -> [CommitHistory] -->
<!-- @RELATION BINDS_TO -> [GitStatusModel] -->
<!-- @RELATION CALLS -> [EXT:frontend:api_module] -->
<!-- @UX_STATE Loading -> Skeleton shown while dashboard detail resolves. -->
<!-- @UX_STATE Loaded -> Metadata, assets, Git controls, and task history rendered. -->
<!-- @UX_STATE Validating -> LLM validation scoped to current dashboard. -->
<!-- @UX_STATE Error -> Inline error with retry. -->
<!-- @UX_FEEDBACK Toast notifications on backup/validation/Git operations. -->
<script lang="ts">
  /**
   * @PURPOSE: Dashboard Detail View - Overview of charts and datasets linked to a dashboard.
   * @LAYER UI
   * @RELATION IMPLEMENTS -> [EXT:frontend:RoutePages]
   * @RELATION IMPLEMENTS -> [PageContracts]
   * @RELATION BINDS_TO -> [NavigationContracts]
   * @RELATION DEPENDS_ON -> [EXT:frontend:api_module]
   * @PRE: Route params include a dashboard reference and environment id before detail, Git, and validation requests are executed.
   * @POST: Dashboard metadata, related assets, and task/Git panels remain synchronized for the selected dashboard context.
   * @SIDE_EFFECT: Loads dashboard, thumbnail, task-history, LLM, and Git data; emits toasts; and may navigate to linked resources.
   * @INVARIANT: Shows dashboard metadata, charts, and datasets for selected environment.
   * @UX_STATE: Loading -> Skeleton and loading feedback remain visible while dashboard detail resolves.
   * @UX_STATE: Loaded -> Dashboard metadata, linked assets, and Git controls render for the selected dashboard.
   * @UX_STATE: Validating -> LLM validation actions stay scoped to the current dashboard context.
   * @UX_STATE: Error -> Inline recovery keeps the user on the current dashboard route.
   * @TEST_DATA: dashboard_detail_ready -> {"dashboard":{"id":11,"title":"Ops","chart_count":3,"dataset_count":2},"taskHistory":[{"id":"t-1","plugin_id":"llm_dashboard_validation","status":"SUCCESS"}],"llmStatus":{"configured":true,"reason":"ok"}}
   * @TEST_DATA: llm_unconfigured -> {"llmStatus":{"configured":false,"reason":"invalid_api_key"}}
   *
   * @TEST_CONTRACT Page_DashboardDetail ->
   * {
   *   required_props: {},
   *   optional_props: {},
   *   invariants: [
   *       "Loads specific dashboard details directly via the API leveraging the id parameter",
   *       "Triggers LLM validation gracefully respecting llmReady status"
   *   ]
   * }
   * @TEST_FIXTURE init_state -> {"id": "1", "env_id": "env1"}
   * @TEST_INVARIANT dashboard_fetching -> verifies: [init_state]
   */

  import { onMount, onDestroy } from "svelte";
  import { goto } from "$app/navigation";
  import { page } from "$app/state";
  import { t } from "$lib/i18n/index.svelte.js";
  import { api } from "$lib/api.js";
  import { GitStatusModel } from "$lib/models/GitStatusModel.svelte.ts";
  import { openDrawerForTaskIfPreferred } from "$lib/stores/taskDrawer.svelte.js";
  import { addToast } from "$lib/toasts.svelte.js";
  import Icon from "$lib/ui/Icon.svelte";
  import BranchSelector from "../../../components/git/BranchSelector.svelte";
  import CommitHistory from "../../../components/git/CommitHistory.svelte";
  import GitManager from "../../../components/git/GitManager.svelte";
  import DashboardHeader from "./components/DashboardHeader.svelte";
  import DashboardGitManager from "./components/DashboardGitManager.svelte";
  import DashboardLinkedResources from "./components/DashboardLinkedResources.svelte";
  import DashboardTaskHistory from "./components/DashboardTaskHistory.svelte";

  let dashboardRef = $derived(page.params.id);
  let envId = $derived(page.url.searchParams.get("env_id") || "");
  let dashboard = $state(null);
  let gitDashboardRef = $derived(dashboard?.slug || dashboardRef || "");
  let resolvedDashboardId = $derived(
    dashboard?.id ??
    (/^\d+$/.test(String(dashboardRef || "")) ? Number(dashboardRef) : null),
  );

  let isLoading = $state(true);
  let error = $state(null);
  let taskHistory = $state([]);
  let isTaskHistoryLoading = $state(false);
  let taskHistoryError = $state(null);
  let isStartingBackup = $state(false);
  let thumbnailUrl = $state("");
  let isThumbnailLoading = $state(false);
  let thumbnailError = $state(null);
  let activeTab = $state("resources");

  // Validation history
  let validationHistory = $state([]);
  let isValidationHistoryLoading = $state(false);
  let validationHistoryError = $state(null);
  let showGitManager = $state(false);
  let wasGitManagerOpen = $state(false);

  const gitModel = new GitStatusModel();

  $effect(() => {
    gitModel.envId = envId || null;
  });

  onMount(async () => {
    await loadDashboardPage();
  });

  onDestroy(() => {
    releaseThumbnailUrl();
  });

  async function loadDashboardPage() {
    await loadDashboardDetail();
    gitModel.dashboardId = gitDashboardRef;
    gitModel.envId = envId || null;
    const effectiveDashboardRef = dashboard?.id ?? dashboardRef;
    await Promise.all([
      loadTaskHistory(effectiveDashboardRef),
      loadThumbnail(false, effectiveDashboardRef),
      loadValidationHistory(effectiveDashboardRef),
      gitModel.loadStatus(),
    ]);
  }

  async function loadDashboardDetail() {
    if (!dashboardRef || !envId) {
      error = $t.dashboard?.missing_context;
      isLoading = false;
      return;
    }

    isLoading = true;
    error = null;
    try {
      dashboard = await api.getDashboardDetail(envId, dashboardRef);
    } catch (err) {
      error = err.message || $t.dashboard?.load_detail_failed;
      console.error("[DashboardDetail][Coherence:Failed]", err);
    } finally {
      isLoading = false;
    }
  }

  async function loadTaskHistory(targetDashboardRef = dashboardRef) {
    if (!targetDashboardRef || !envId) return;
    isTaskHistoryLoading = true;
    taskHistoryError = null;
    try {
      const response = await api.getDashboardTaskHistory(envId, targetDashboardRef, {
        limit: 30,
      });
      taskHistory = response?.items || [];
    } catch (err) {
      taskHistoryError = err.message || "Failed to load task history";
      taskHistory = [];
    } finally {
      isTaskHistoryLoading = false;
    }
  }

  function releaseThumbnailUrl() {
    if (thumbnailUrl) {
      URL.revokeObjectURL(thumbnailUrl);
      thumbnailUrl = "";
    }
  }

  async function loadThumbnail(force = false, targetDashboardRef = dashboardRef) {
    if (!targetDashboardRef || !envId) return;
    isThumbnailLoading = true;
    thumbnailError = null;
    try {
      const blob = await api.getDashboardThumbnail(envId, targetDashboardRef, {
        force,
      });
      releaseThumbnailUrl();
      thumbnailUrl = URL.createObjectURL(blob);
    } catch (err) {
      if (err?.status === 202) {
        thumbnailError =
          $t.dashboard?.thumbnail_generating || "Thumbnail is being generated";
      } else {
        thumbnailError =
          err.message ||
          $t.dashboard?.thumbnail_failed ||
          "Failed to load thumbnail";
      }
    } finally {
      isThumbnailLoading = false;
    }
  }

  async function runBackupTask() {
    if (isStartingBackup || !envId || !resolvedDashboardId) return;
    isStartingBackup = true;
    try {
      const response = await api.postApi("/dashboards/backup", {
        env_id: envId,
        dashboard_ids: [resolvedDashboardId],
      });
      const taskId = response?.task_id;
      if (taskId) {
        openDrawerForTaskIfPreferred(taskId);
        addToast(
          $t.dashboard?.backup_started || "Backup task started",
          "success",
        );
      }
      await loadTaskHistory();
    } catch (err) {
      addToast(
        err.message ||
          $t.dashboard?.backup_task_failed ||
          "Failed to start backup",
        "error",
      );
    } finally {
      isStartingBackup = false;
    }
  }

  // (runLlmValidationTask and openLlmReport removed — replaced by Validation History section)

  async function loadValidationHistory(targetDashboardRef = dashboardRef) {
    if (!targetDashboardRef || !envId) return;
    isValidationHistoryLoading = true;
    validationHistoryError = null;
    try {
      const qs = new URLSearchParams();
      qs.append('dashboard_id', String(targetDashboardRef));
      qs.append('environment_id', envId);
      qs.append('page_size', '10');
      const { fetchApi } = await import('$lib/api');
      const response = await fetchApi(`/validation-tasks/runs/all?${qs.toString()}`);
      validationHistory = response?.items || response?.results || [];
    } catch (err) {
      validationHistoryError = err.message || "Failed to load validation history";
      validationHistory = [];
    } finally {
      isValidationHistoryLoading = false;
    }
  }

  function toTaskTypeLabel(pluginId) {
    if (pluginId === "superset-backup") return $t.dashboard?.backup || "Backup";
    if (pluginId === "llm_dashboard_validation")
      return $t.dashboard?.llm_check || "LLM Check";
    return pluginId || "-";
  }

  function getTaskStatusClasses(status) {
    const normalized = (status || "").toLowerCase();
    if (normalized === "running" || normalized === "pending")
      return "bg-blue-100 text-blue-700";
    if (normalized === "success") return "bg-emerald-100 text-emerald-700";
    if (normalized === "failed" || normalized === "error")
      return "bg-rose-100 text-rose-700";
    if (normalized === "awaiting_input" || normalized === "waiting_input")
      return "bg-amber-100 text-amber-700";
    return "bg-slate-100 text-slate-700";
  }

  function getValidationStatus(task) {
    if (task?.plugin_id !== "llm_dashboard_validation") {
      return { label: "-", level: "na", icon: "" };
    }
    const rawStatus = String(task?.validation_status || "").toUpperCase();
    if (rawStatus === "FAIL") {
      return { label: "FAIL", level: "fail", icon: "!" };
    }
    if (rawStatus === "WARN") {
      return { label: "WARN", level: "warn", icon: "!" };
    }
    if (rawStatus === "PASS") {
      return { label: "PASS", level: "pass", icon: "OK" };
    }
    return { label: "UNKNOWN", level: "unknown", icon: "?" };
  }

  function getValidationStatusClasses(level) {
    if (level === "fail") return "bg-rose-100 text-rose-700 border-rose-200";
    if (level === "warn") return "bg-amber-100 text-amber-700 border-amber-200";
    if (level === "pass")
      return "bg-emerald-100 text-emerald-700 border-emerald-200";
    if (level === "unknown")
      return "bg-slate-100 text-slate-700 border-slate-200";
    return "bg-slate-50 text-slate-400 border-slate-200";
  }

  function goBack() {
    goto(`/dashboards?env_id=${encodeURIComponent(envId)}`);
  }

  function openDataset(datasetId) {
    goto(`/datasets/${datasetId}?env_id=${encodeURIComponent(envId)}`);
  }

  function formatDate(value) {
    if (!value) return "-";
    const parsed = new Date(value);
    if (Number.isNaN(parsed.getTime())) return "-";
    return `${parsed.toLocaleDateString()} ${parsed.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
  }

  async function handleSyncAndOpenCommit() {
    const success = await gitModel.syncRepository();
    if (success) showGitManager = true;
  }

  $effect(() => {
    if (showGitManager) {
      wasGitManagerOpen = true;
      return;
    }
    if (!wasGitManagerOpen) return;
    wasGitManagerOpen = false;
    void gitModel.loadStatus();
  });
</script>

<div class="mx-auto w-full max-w-7xl space-y-6">
  <DashboardHeader
    {dashboard}
    {resolvedDashboardId}
    {dashboardRef}
    {envId}
    {gitDashboardRef}
    hasGitRepo={gitModel.hasGitRepo}
    bind:currentBranch={gitModel.currentBranch}
    gitMeta={gitModel.meta}
    gitStatus={gitModel.gitStatus}
    {isStartingBackup}
    bind:showGitManager
    {goBack}
    handleBranchChange={(e) => gitModel.handleBranchChange(e)}
    {runBackupTask}
    {loadDashboardPage}
  />

  {#if error}
    <div
      class="flex items-center justify-between rounded-lg border border-red-300 bg-destructive-light px-4 py-3 text-red-700"
    >
      <span>{error}</span>
      <button
        class="rounded bg-destructive px-3 py-1.5 text-white hover:bg-destructive-hover"
        onclick={loadDashboardDetail}
      >
        {$t.common?.retry}
      </button>
    </div>
  {/if}

  {#if isLoading}
    <div class="grid grid-cols-1 gap-4 md:grid-cols-3">
      {#each Array(3) as _}
        <div
          class="h-24 animate-pulse rounded-xl border border-slate-200 bg-white"
        ></div>
      {/each}
    </div>
    <div
      class="h-64 animate-pulse rounded-xl border border-slate-200 bg-white"
    ></div>
  {:else if dashboard}
    <div class="grid grid-cols-1 gap-6 xl:grid-cols-5">
      <div class="rounded-xl border border-slate-200 bg-white p-4 xl:col-span-2">
        <div class="mb-3 flex items-center justify-between">
          <h2 class="text-sm font-semibold uppercase tracking-wide text-slate-500">
            {$t.dashboard?.api_thumbnail || "Dashboard thumbnail"}
          </h2>
          <button
            class="rounded-md border border-slate-300 px-2 py-1 text-xs text-slate-700 hover:bg-slate-50"
            onclick={() => loadThumbnail(true)}
            disabled={isThumbnailLoading}
          >
            {$t.common?.refresh || "Refresh"}
          </button>
        </div>
        {#if isThumbnailLoading}
          <div class="h-56 animate-pulse rounded-lg bg-slate-100"></div>
        {:else if thumbnailUrl}
          <img
            src={thumbnailUrl}
            alt="Dashboard thumbnail"
            class="h-56 w-full rounded-lg border border-slate-200 object-cover"
          />
        {:else}
          <div
            class="flex h-56 items-center justify-center rounded-lg border border-dashed border-slate-300 bg-slate-50 text-sm text-slate-500"
          >
            {thumbnailError ||
              $t.dashboard?.thumbnail_unavailable ||
              "Thumbnail is unavailable"}
          </div>
        {/if}
      </div>

      <DashboardGitManager
        isGitStatusLoading={gitModel.statusLoading}
        gitStatusError={gitModel.statusError}
        hasGitRepo={gitModel.hasGitRepo}
        gitSyncState={gitModel.syncState}
        gitStatus={gitModel.gitStatus}
        changedChartsCount={gitModel.changedChartsCount}
        changedDatasetsCount={gitModel.changedDatasetsCount}
        allChangedFiles={gitModel.allChangedFilesList}
        runGitSyncAndOpenCommit={handleSyncAndOpenCommit}
        isSyncingGit={gitModel.syncing}
        hasChangesToCommit={gitModel.hasChangesToCommit}
        loadGitDiffPreview={() => gitModel.loadDiffPreview()}
        isGitDiffLoading={gitModel.diffLoading}
        runGitPull={() => gitModel.pullRepository()}
        isPullingGit={gitModel.pulling}
        runGitPush={() => gitModel.pushRepository()}
        isPushingGit={gitModel.pushing}
        gitDiffPreview={gitModel.diffPreview}
        loadGitStatus={() => gitModel.loadStatus()}
        bind:showGitManager
      />
    </div>

    <div class="grid grid-cols-1 gap-4 md:grid-cols-3">
      <div class="rounded-xl border border-slate-200 bg-white p-4">
        <p class="text-xs font-semibold uppercase tracking-wide text-slate-500">
          {$t.dashboard?.last_modified}
        </p>
        <p class="mt-2 text-lg font-semibold text-slate-900">
          {formatDate(dashboard.last_modified)}
        </p>
      </div>
      <div class="rounded-xl border border-slate-200 bg-white p-4">
        <p class="text-xs font-semibold uppercase tracking-wide text-slate-500">
          {$t.dashboard?.charts}
        </p>
        <p class="mt-2 text-lg font-semibold text-slate-900">
          {dashboard.chart_count || 0}
        </p>
      </div>
      <div class="rounded-xl border border-slate-200 bg-white p-4">
        <p class="text-xs font-semibold uppercase tracking-wide text-slate-500">
          {$t.nav?.datasets}
        </p>
        <p class="mt-2 text-lg font-semibold text-slate-900">
          {dashboard.dataset_count || 0}
        </p>
      </div>
    </div>

    <!-- Validation History Section -->
    <div class="rounded-xl border border-slate-200 bg-white p-4">
      <div class="mb-3 flex items-center justify-between">
        <h2 class="text-sm font-semibold uppercase tracking-wide text-slate-500">
          {$t.dashboard?.validation_history || "Validation History"}
        </h2>
        <button
          class="rounded-md border border-slate-300 px-2 py-1 text-xs text-slate-700 hover:bg-slate-50"
          onclick={() => loadValidationHistory(dashboard?.id ?? dashboardRef)}
          disabled={isValidationHistoryLoading}
        >
          {$t.common?.refresh}
        </button>
      </div>
      {#if isValidationHistoryLoading}
        <div class="space-y-2">
          {#each Array(3) as _}
            <div class="h-10 animate-pulse rounded bg-slate-100"></div>
          {/each}
        </div>
      {:else if validationHistoryError}
        <div class="rounded-lg border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700">
          {validationHistoryError}
        </div>
      {:else if validationHistory.length === 0}
        <div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-6 text-center text-sm text-slate-500">
          {$t.dashboard?.no_validation_runs || "No validation runs yet"}
        </div>
      {:else}
        <div class="overflow-x-auto">
          <table class="min-w-full divide-y divide-slate-200 text-sm">
            <thead class="bg-slate-50">
              <tr>
                <th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.common?.date}</th>
                <th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.tasks?.name || "Task Name"}</th>
                <th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.common?.status}</th>
                <th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.common?.actions}</th>
              </tr>
            </thead>
            <tbody class="divide-y divide-slate-100">
              {#each validationHistory as run}
                {@const runStatus = String(run.status || "").toUpperCase()}
                <tr>
                  <td class="px-3 py-2 text-slate-700">{formatDate(run.timestamp || run.created_at || run.started_at)}</td>
                  <td class="px-3 py-2 text-slate-800">{run.summary ? run.summary.slice(0, 80) + (run.summary.length > 80 ? '…' : '') : '-'}</td>
                  <td class="px-3 py-2">
                    <span
                      class={`rounded-full px-2 py-1 text-xs font-semibold uppercase ${runStatus === "SUCCESS" || runStatus === "PASS" ? "bg-emerald-100 text-emerald-700" : runStatus === "FAIL" || runStatus === "FAILED" || runStatus === "ERROR" ? "bg-rose-100 text-rose-700" : runStatus === "WARN" ? "bg-amber-100 text-amber-700" : runStatus === "RUNNING" || runStatus === "PENDING" ? "bg-blue-100 text-blue-700" : "bg-slate-100 text-slate-700"}`}
                    >
                      {runStatus || "UNKNOWN"}
                    </span>
                  </td>
                  <td class="px-3 py-2">
                    {#if run.policy_id || run.policyId}
                      <a
                        href={`/validation-tasks/${run.policy_id || run.policyId}/runs/${run.id || run.run_id}`}
                        class="inline-flex items-center gap-1 rounded-md border border-indigo-300 bg-indigo-50 px-2 py-1 text-xs text-indigo-700 hover:bg-indigo-100"
                      >
                        {$t.common?.view_report || "Report"}
                      </a>
                    {:else if run.id}
                      <button
                        class="inline-flex items-center gap-1 rounded-md border border-slate-300 px-2 py-1 text-xs text-slate-600 hover:bg-slate-50"
                        onclick={() => openDrawerForTaskIfPreferred(run.id)}
                      >
                        {$t.tasks?.open_task_drawer || "View task"}
                      </button>
                    {/if}
                  </td>
                </tr>
              {/each}
            </tbody>
          </table>
        </div>
        <!-- View all link -->
        <div class="border-t border-slate-100 px-3 py-2 text-right">
          <a
            href={`/dashboards/${encodeURIComponent(String(resolvedDashboardId ?? dashboardRef))}/validation?env_id=${encodeURIComponent(envId || '')}`}
            class="inline-flex items-center gap-1 text-xs font-medium text-indigo-600 hover:text-indigo-800 transition-colors"
          >
            {$t.dashboard?.view_all_validation || 'View all validation runs'}
            <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
            </svg>
          </a>
        </div>
      {/if}
    </div>

    <div class="rounded-xl border border-slate-200 bg-white">
      <div class="flex flex-wrap items-center gap-2 border-b border-slate-200 px-4 py-3">
        <button
          class={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${activeTab === "resources" ? "bg-slate-900 text-white" : "bg-slate-100 text-slate-700 hover:bg-slate-200"}`}
          onclick={() => (activeTab = "resources")}
        >
          {$t.dashboard?.linked_resources || "Linked resources"}
        </button>
        <button
          class={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${activeTab === "git-history" ? "bg-slate-900 text-white" : "bg-slate-100 text-slate-700 hover:bg-slate-200"}`}
          onclick={() => (activeTab = "git-history")}
        >
          {$t.dashboard?.git_history || "Git history"}
        </button>

      </div>

      <div class="space-y-4 p-4">
        {#if activeTab === "resources"}
          <DashboardLinkedResources
            {dashboard}
            {openDataset}
            {formatDate}
          />
        {:else if activeTab === "git-history"}
          {#if gitModel.hasGitRepo && gitDashboardRef}
            <CommitHistory dashboardId={gitDashboardRef} envId={envId || null} />
          {:else}
            <div class="rounded-lg border border-slate-200 bg-slate-50 px-4 py-6 text-sm text-slate-600">
              {$t.git?.not_linked || "This dashboard is not yet linked to a Git repository."}
            </div>
          {/if}
        {/if}
      </div>
    </div>
  {/if}
</div>

{#if showGitManager && gitDashboardRef}
  <GitManager
    dashboardId={gitDashboardRef}
    envId={envId || null}
    dashboardTitle={dashboard?.title || ""}
    bind:show={showGitManager}
  />
{/if}

<!-- #endregion DashboardDetail -->
