<!-- #region GitDashboardPage [C:4] [TYPE Page] [SEMANTICS sveltekit, git, dashboard, environment, repository] -->
<!-- @ingroup Routes -->
<!-- @BRIEF Git integration page — tabbed view for all dashboards and repository-only management. -->
<!-- @LAYER Page -->
<!-- @RELATION DEPENDS_ON -> [EXT:frontend:RepositoryDashboardGrid] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:environmentContextStore] -->
<!-- @UX_STATE Loading -> Environment and dashboard data are being resolved. -->
<!-- @UX_STATE Ready -> Git dashboard grid and environment selector are interactive. -->
<!-- @UX_STATE Refreshing -> Changing environment refreshes dashboard data while preserving page context. -->
<!-- @UX_STATE Error -> Toast-driven failure feedback preserves filter selection. -->
<!-- @UX_FEEDBACK Toast notifications on API errors. -->
<!-- @UX_RECOVERY User can retry by changing environment or refreshing. -->
<!-- @INVARIANT Tab switch resets selectedIds and currentPage in the grid. -->
<!-- @INVARIANT "Repositories" tab only shows dashboards with initialized Git repos. -->
<script lang="ts">
import { SvelteSet } from "svelte/reactivity";
  import { onMount, untrack } from 'svelte';
  import RepositoryDashboardGrid from '$lib/components/dashboard/RepositoryDashboardGrid.svelte';
  import { addToast as toast } from '$lib/toasts.svelte.js';
  import { api } from '../../lib/api.js';
  import { gitService } from '../../services/gitService.js';
  import type { DashboardMetadata } from '../../types/dashboard';
  import { t } from '$lib/i18n/index.svelte.js';
  import { Card, PageHeader, Select } from '$lib/ui';
  import {
    environmentContextStore,
    initializeEnvironmentContext,
    setSelectedEnvironment,
  } from '$lib/stores/environmentContext.svelte.js';

  type TabId = "all" | "repos";

  let selectedEnvId = $state("");
  let activeTab: TabId = $state("all");
  let allDashboards: DashboardMetadata[] = $state([]);
  let repositoriesOnlyDashboards: DashboardMetadata[] = $state([]);
  let loading = $state(true);
  let fetchingDashboards = $state(false);
  let fetchingRepos = $state(false);
  let environments = $derived(
    environmentContextStore.value?.environments || [],
  );

  // ── Derived: which dashboards to show based on active tab ──
  let dashboards = $derived(
    activeTab === "repos" ? repositoriesOnlyDashboards : allDashboards,
  );

  async function fetchEnvironments() {
    try {
      await initializeEnvironmentContext();
      selectedEnvId = environmentContextStore.value?.selectedEnvId || "";
    } catch (e) {
      toast(e.message, 'error');
    } finally {
      loading = false;
    }
  }

  async function fetchAllDashboards(envId: string) {
    if (!envId) return;
    fetchingDashboards = true;
    try {
      const pageSize = 100;
      let page = 1;
      let aggregated: DashboardMetadata[] = [];
      let totalPages = 1;

      while (page <= totalPages) {
        const response = await api.requestApi(
          `/dashboards?env_id=${encodeURIComponent(envId)}&page=${page}&page_size=${pageSize}`,
        );
        const pageDashboards = Array.isArray(response?.dashboards)
          ? response.dashboards
          : [];
        aggregated = aggregated.concat(pageDashboards);
        totalPages = Number(response?.total_pages || 1);
        page += 1;
      }

      allDashboards = aggregated;
    } catch (e) {
      toast(e.message, 'error');
      allDashboards = [];
    } finally {
      fetchingDashboards = false;
    }
  }

  async function fetchRepositoriesOnly(envId: string) {
    if (!envId) return;
    fetchingRepos = true;
    try {
      // Reuse allDashboards if already fetched, otherwise fetch first
      let source = allDashboards;
      if (source.length === 0) {
        const pageSize = 100;
        let page = 1;
        let aggregated: DashboardMetadata[] = [];
        let totalPages = 1;

        while (page <= totalPages) {
          const response = await api.requestApi(
            `/dashboards?env_id=${encodeURIComponent(envId)}&page=${page}&page_size=${pageSize}`,
          );
          const pageDashboards = Array.isArray(response?.dashboards)
            ? response.dashboards
            : [];
          aggregated = aggregated.concat(pageDashboards);
          totalPages = Number(response?.total_pages || 1);
          page += 1;
        }
        source = aggregated;
        allDashboards = aggregated;
      }

      // Filter to only dashboards with initialized repos
      const chunkSize = 50;
      const repoIds = new SvelteSet<number>();
      const allIds = source.map((d) => d.id);

      for (let offset = 0; offset < allIds.length; offset += chunkSize) {
        const idsChunk = allIds.slice(offset, offset + chunkSize);
        try {
          const batchResponse = await gitService.getStatusesBatch(idsChunk);
          const statuses = batchResponse?.statuses || {};
          idsChunk.forEach((dashboardId) => {
            const status = statuses[dashboardId] || statuses[String(dashboardId)];
            const syncStatus = String(status?.sync_status || status?.sync_state || "").toUpperCase();
            if (syncStatus && syncStatus !== "NO_REPO") {
              repoIds.add(dashboardId);
            }
          });
        } catch (error) {
          console.error(
            `[GitPage] Failed to resolve repository statuses chunk: ${error instanceof Error ? error.message : error}`,
          );
        }
      }

      repositoriesOnlyDashboards = source.filter((d) => repoIds.has(d.id));
    } catch (e) {
      toast(e.message, 'error');
      repositoriesOnlyDashboards = [];
    } finally {
      fetchingRepos = false;
    }
  }

  async function fetchDashboards(envId: string) {
    if (!envId) return;
    await fetchAllDashboards(envId);
    await fetchRepositoriesOnly(envId);
  }

  function handleTabChange(tab: TabId) {
    if (activeTab === tab) return;
    activeTab = tab;
  }

  onMount(fetchEnvironments);

  // REASON: Single effect to sync store-selectedEnvId → local selectedEnvId on init.
  // Removed setSelectedEnvironment from the fetch effect — it updated the store,
  // which re-triggered the sync effect, creating an infinite loop (2300+ iterations).
  // Persistence is now handled via the Select's onchange handler below.
  $effect(() => {
    const sid = environmentContextStore.value?.selectedEnvId;
    if (sid) {
      const localEnvId = untrack(() => selectedEnvId);
      if (localEnvId !== sid) {
        selectedEnvId = sid;
      }
    }
  });

  $effect(() => {
    if (!selectedEnvId) return;
    void fetchDashboards(selectedEnvId);
  });
</script>

<div class="max-w-6xl mx-auto p-6">
  <PageHeader title={$t.git?.management}>
    <div slot="actions" class="flex items-center space-x-4">
      <Select
        label={$t.dashboard?.environment}
        bind:value={selectedEnvId}
        options={environments.map(e => ({ value: e.id, label: e.name }))}
        onchange={() => { if (selectedEnvId) setSelectedEnvironment(selectedEnvId); }}
      />
    </div>
  </PageHeader>

  {#if loading}
    <div class="flex justify-center py-12">
      <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-ring"></div>
    </div>
  {:else}
    <!-- Tab bar -->
    <div class="mb-4 border-b border-border">
      <nav class="flex gap-1 -mb-px" aria-label="Git view tabs">
        <button
          type="button"
          class="px-4 py-2.5 text-sm font-medium transition-colors
            {activeTab === 'all'
              ? 'border-b-2 border-primary text-primary'
              : 'text-text-muted hover:text-text hover:border-border-strong'}"
          onclick={() => handleTabChange("all")}
          aria-selected={activeTab === 'all'}
          role="tab"
        >
          {$t.git?.management || "All Dashboards"}
          {#if allDashboards.length > 0}
            <span class="ml-1.5 text-xs text-text-subtle">({allDashboards.length})</span>
          {/if}
        </button>
        <button
          type="button"
          class="px-4 py-2.5 text-sm font-medium transition-colors
            {activeTab === 'repos'
              ? 'border-b-2 border-primary text-primary'
              : 'text-text-muted hover:text-text hover:border-border-strong'}"
          onclick={() => handleTabChange("repos")}
          aria-selected={activeTab === 'repos'}
          role="tab"
        >
          {$t.nav?.repositories || "Repositories"}
          {#if repositoriesOnlyDashboards.length > 0}
            <span class="ml-1.5 text-xs text-text-subtle">({repositoriesOnlyDashboards.length})</span>
          {/if}
        </button>
      </nav>
    </div>

    <Card title={activeTab === 'repos' ? ($t.nav?.repositories || "Repositories") : ($t.git?.select_dashboard || "Select Dashboard to Manage")}>
      {#if fetchingDashboards || fetchingRepos}
        <p class="text-text-muted">{$t.common?.loading}</p>
      {:else if dashboards.length > 0}
        <RepositoryDashboardGrid
          {dashboards}
          statusMode="repository"
          envId={selectedEnvId || null}
          repositoriesOnly={activeTab === 'repos'}
        />
      {:else}
        <p class="text-text-muted italic">
          {activeTab === 'repos'
            ? ($t.git?.no_repositories_selected || "No repositories found")
            : ($t.dashboard?.no_dashboards || "No dashboards")}
        </p>
      {/if}
    </Card>
  {/if}
</div>
<!-- #endregion GitDashboardPage -->
