<!-- #region Dashboard.RepositoryGrid [C:3] [TYPE Component] [SEMANTICS dashboard,grid,pagination,selection,repository,git] -->
<!-- @ingroup Dashboard -->
<!-- @BRIEF Dashboard grid with git repository integration — status fetching, bulk git actions, GitManager modal. -->
<!-- @LAYER UI -->
<!-- @RATIONALE Now delegates grid rendering to Dashboard.DataGrid. Keeps ~200 lines of git-specific logic (status fetching via gitService, bulk git operations, GitManager modal). Filter/sort/paginate/select/selection tracking handled by Dashboard.DataGrid. -->
<!-- @RELATION USED_BY -> [frontend/src/routes/git/+page.svelte] -->
<!-- @RELATION DEPENDS_ON -> [$lib/components/git/GitManager] -->
<!-- @RELATION DEPENDS_ON -> [gitService] -->
<!-- @RELATION DEPENDS_ON -> [Dashboard.DataGrid] -->
<!-- @INVARIANT Selected IDs must be a subset of available dashboards. -->
<script lang="ts">
  // [SECTION: IMPORTS]
  import { untrack } from "svelte";
  import type { DashboardMetadata } from "../../types/dashboard";
  import type { Column } from "./DashboardDataGrid.svelte";
  import { t } from '$lib/i18n/index.svelte.js';
  import { Button, ConfirmDialog } from "$lib/ui";
  import DashboardDataGrid from "./DashboardDataGrid.svelte";
  import GitManager from "$lib/components/git/GitManager.svelte";
  import { gitService } from "../../../services/gitService";
  import { addToast } from "$lib/toasts.svelte.js";
  // [/SECTION]

  // [SECTION: PROPS]
  let {
    dashboards = [] as DashboardMetadata[],
    selectedIds = [] as number[],
    statusMode = "dashboard" as "dashboard" | "repository",
    envId = null as string | null,
    repositoriesOnly = false,
  } = $props();
  // [/SECTION]

  // [SECTION: STATE]
  let filterText = $state("");
  let currentPage = $state(0);
  let sortColumn = $state("title");
  let sortDirection: "asc" | "desc" = $state("asc");
  let showGitManager = $state(false);
  let gitDashboardId: string | null = $state(null);
  let gitDashboardTitle = $state("");
  let repositoryStatusByDashboardId = $state<Record<number, string>>({});
  let repositoryStatusRequestId = $state(0);
  let bulkActionRunning = $state(false);
  let showBulkDeleteConfirm = $state(false);
  // [/SECTION]

  // ── Column definitions ─────────────────────────────────────────
  let columns: Column[] = $derived([
    { key: "title", label: $t.dashboard?.title || "Title", sortable: true },
    { key: "last_modified", label: $t.dashboard?.last_modified || "Last Modified", sortable: true },
    { key: "status", label: $t.dashboard?.status || "Status", sortable: true, raw: true, render: (item: DashboardMetadata) => renderStatusBadge(item) },
  ]);

  // ── Actions per row ────────────────────────────────────────────
  let rowActions = $derived([
    {
      label: $t.git?.manage || "Git",
      variant: "ghost" as const,
      handler: (item: DashboardMetadata) => openGitManagerForDashboard(item),
      condition: (item: DashboardMetadata) => statusMode === "repository",
    },
  ]);

  // ── Pre-filtered data (repositoriesOnly) ──────────────────────
  let preFilteredData = $derived<DashboardMetadata[]>(
    repositoriesOnly && statusMode === "repository"
      ? dashboards.filter(d => getRepositoryStatusToken(d.id) !== "no_repo")
      : dashboards,
  );

  // [SECTION: GIT-SPECIFIC FUNCTIONS]

  // #region getRepositoryStatusToken:Function [TYPE Function]
  function getRepositoryStatusToken(dashboardId: number): string {
    return repositoryStatusByDashboardId[dashboardId] || "loading";
  }
  // #endregion getRepositoryStatusToken:Function

  // #region isRepositoryReady:Function [TYPE Function]
  function isRepositoryReady(dashboardId: number): boolean {
    const token = getRepositoryStatusToken(dashboardId);
    return token !== "loading" && token !== "no_repo" && token !== "error";
  }
  // #endregion isRepositoryReady:Function

  // #region invalidateRepositoryStatuses:Function [TYPE Function]
  function invalidateRepositoryStatuses(dashboardIds: number[]): void {
    if (dashboardIds.length === 0) return;
    const nextStatuses = { ...repositoryStatusByDashboardId };
    dashboardIds.forEach((did) => { nextStatuses[did] = "loading"; });
    repositoryStatusByDashboardId = nextStatuses;
  }
  // #endregion invalidateRepositoryStatuses:Function

  // #region resolveRepositoryStatusToken:Function [TYPE Function]
  function resolveRepositoryStatusToken(status: any): string {
    const syncState = String(status?.sync_state || "").toUpperCase();
    if (syncState === "DIVERGED") return "diverged";
    if (syncState === "BEHIND_REMOTE") return "behind_remote";
    if (syncState === "AHEAD_REMOTE") return "ahead_remote";
    if (syncState === "CHANGES") return "changes";
    if (syncState === "SYNCED") return "synced";
    const syncStatus = String(status?.sync_status || "").toUpperCase();
    if (syncStatus === "NO_REPO") return "no_repo";
    if (syncStatus === "ERROR") return "error";
    if (syncStatus === "DIFF") return "changes";
    if (syncStatus === "OK") return "synced";
    const aheadCount = Number(status?.ahead_count || 0);
    const behindCount = Number(status?.behind_count || 0);
    if (aheadCount > 0 && behindCount > 0) return "diverged";
    if (behindCount > 0) return "behind_remote";
    if (aheadCount > 0) return "ahead_remote";
    const hasChanges =
      Boolean(status?.is_dirty) ||
      (status?.untracked_files?.length || 0) > 0 ||
      (status?.modified_files?.length || 0) > 0 ||
      (status?.staged_files?.length || 0) > 0;
    return hasChanges ? "changes" : "synced";
  }
  // #endregion resolveRepositoryStatusToken:Function

  // #region loadRepositoryStatuses:Function [TYPE Function]
  async function loadRepositoryStatuses() {
    if (statusMode !== "repository") {
      repositoryStatusByDashboardId = {};
      return;
    }
    const requestId = ++repositoryStatusRequestId;
    // Load statuses for all pre-filtered data (not just current page — DashboardDataGrid owns pagination)
    const allIds = dashboards.map(d => d.id);
    const missingIds = allIds.filter(id => {
      const tkn = repositoryStatusByDashboardId[id];
      return tkn === undefined || tkn === "loading";
    });
    if (missingIds.length === 0) return;
    const loadingStatuses = { ...repositoryStatusByDashboardId };
    missingIds.forEach(id => { loadingStatuses[id] = "loading"; });
    repositoryStatusByDashboardId = loadingStatuses;
    let entries: Array<readonly [number, string]> = [];
    try {
      const batchResult = await gitService.getStatusesBatch(missingIds);
      const statusesByDashboardId = batchResult?.statuses || {};
      entries = missingIds.map((id) => {
        const s = statusesByDashboardId[id] ?? statusesByDashboardId[String(id)];
        return [id, s ? resolveRepositoryStatusToken(s) : "error"] as const;
      });
    } catch {
      entries = missingIds.map((id) => [id, "error"] as const);
    }
    if (requestId !== repositoryStatusRequestId) return;
    repositoryStatusByDashboardId = {
      ...repositoryStatusByDashboardId,
      ...Object.fromEntries(entries),
    };
  }
  // #endregion loadRepositoryStatuses:Function

  // #region runBulkGitAction:Function [TYPE Function]
  async function runBulkGitAction(actionToken: string, action: (_id: number) => Promise<void>): Promise<void> {
    if (bulkActionRunning) return;
    const readyIds = selectedIds.filter((id) => isRepositoryReady(id));
    if (readyIds.length === 0) { addToast($t.git?.no_repositories_selected, "error"); return; }
    bulkActionRunning = true;
    const concurrency = 3;
    const queue = [...readyIds];
    let successCount = 0, failedCount = 0;
    const worker = async () => {
      while (queue.length > 0) {
        const id = queue.shift();
        if (id === undefined) break;
        try { await action(id); successCount++; }
        catch { failedCount++; }
      }
    };
    try {
      await Promise.all(Array.from({ length: Math.min(concurrency, readyIds.length) }, () => worker()));
      invalidateRepositoryStatuses(readyIds);
      const al = $t.git?.[`bulk_action_${actionToken}`] || actionToken;
      addToast($t.git?.bulk_result.replace("{action}", al).replace("{success}", String(successCount)).replace("{failed}", String(failedCount)),
        failedCount > 0 ? "warning" : "success");
    } finally { bulkActionRunning = false; }
  }
  // #endregion runBulkGitAction:Function

  // #region handleBulkAction:Function [TYPE Function]
  async function handleBulkSync() { await runBulkGitAction("sync", (id) => gitService.sync(id, null, envId)); }
  async function handleBulkCommit() {
    const msg = prompt($t.git?.commit_message);
    if (!msg?.trim()) return;
    await runBulkGitAction("commit", (id) => gitService.commit(id, msg.trim(), [], envId));
  }
  async function handleBulkPull() { await runBulkGitAction("pull", (id) => gitService.pull(id, envId)); }
  async function handleBulkPush() { await runBulkGitAction("push", (id) => gitService.push(id, envId)); }
  async function onConfirmBulkDelete() {
    const idsToDelete = [...selectedIds];
    await runBulkGitAction("delete", (id) => gitService.deleteRepository(id, envId));
    dashboards = dashboards.filter((d) => !idsToDelete.includes(d.id));
    selectedIds = [];
  }
  // #endregion handleBulkAction:Function

  // #region handleManageSelected:Function [TYPE Function]
  function handleManageSelected(): void {
    if (selectedIds.length !== 1) { addToast($t.git?.select_single_for_manage, "warning"); return; }
    const d = dashboards.find(dash => dash.id === selectedIds[0]);
    openGitManagerForDashboard(d || null);
  }
  // #endregion handleManageSelected:Function

  // #region handleInitializeRepositories:Function [TYPE Function]
  function handleInitializeRepositories(): void {
    if (selectedIds.length !== 1) { addToast($t.git?.select_single_for_manage, "warning"); return; }
    const d = dashboards.find(dash => dash.id === selectedIds[0]) || null;
    openGitManagerForDashboard(d);
  }
  // #endregion handleInitializeRepositories:Function

  // #region resolveDashboardRef:Function [TYPE Function]
  function resolveDashboardRef(dashboard: DashboardMetadata): string | null {
    const directSlug = String(dashboard.slug || dashboard.dashboard_slug || dashboard.url_slug || "").trim();
    if (directSlug) return directSlug;
    const dashboardUrl = String(dashboard.url || "").trim();
    if (!dashboardUrl) return null;
    const slugMatch = dashboardUrl.match(/\/dashboard\/([^/?#]+)/i);
    return slugMatch ? decodeURIComponent(slugMatch[1]) : null;
  }
  // #endregion resolveDashboardRef:Function

  // #region openGitManagerForDashboard:Function [TYPE Function]
  function openGitManagerForDashboard(dashboard: DashboardMetadata | null): void {
    if (!dashboard) return;
    const ref = resolveDashboardRef(dashboard);
    if (!ref) { addToast($t.git?.select_dashboard_with_slug || "Dashboard slug required", "error"); return; }
    gitDashboardId = ref;
    gitDashboardTitle = dashboard.title || "";
    showGitManager = true;
  }
  // #endregion openGitManagerForDashboard:Function

  // #region getSortStatusValue:Function [TYPE Function]
  function getSortStatusValue(dashboard: DashboardMetadata): string {
    if (statusMode === "repository") return getRepositoryStatusToken(dashboard.id);
    return String(dashboard.status || "").toLowerCase();
  }
  // #endregion getSortStatusValue:Function

  // #region getStatusLabel:Function [TYPE Function]
  function getStatusLabel(dashboard: DashboardMetadata): string {
    if (statusMode !== "repository") return String(dashboard.status || "");
    const token = getRepositoryStatusToken(dashboard.id);
    return $t.git?.repo_status?.[token] || token;
  }
  // #endregion getStatusLabel:Function

  // #region getStatusBadgeClass:Function [TYPE Function]
  function getStatusBadgeClass(dashboard: DashboardMetadata): string {
    if (statusMode !== "repository") {
      return dashboard.status === "published" ? "bg-success-light text-success" : "bg-surface-muted text-text";
    }
    const token = getRepositoryStatusToken(dashboard.id);
    if (token === "loading" || token === "no_repo") return "bg-surface-muted text-text-muted";
    if (token === "synced") return "bg-success-light text-success";
    if (token === "changes") return "bg-warning-light text-warning";
    if (token === "behind_remote" || token === "ahead_remote") return "bg-primary-light text-primary";
    if (token === "diverged") return "bg-info-light text-info";
    return "bg-destructive-light text-destructive";
  }
  // #endregion getStatusBadgeClass:Function

  // #region renderStatusBadge:Function [TYPE Function]
  function renderStatusBadge(dashboard: DashboardMetadata): string {
    const classes = getStatusBadgeClass(dashboard);
    const label = getStatusLabel(dashboard);
    return `<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${classes}">${label}</span>`;
  }
  // #endregion renderStatusBadge:Function

  // ── Reactive: load repository statuses ─────────────────────────
  $effect(() => {
    dashboards;
    statusMode;
    untrack(() => { void loadRepositoryStatuses(); });
  });
</script>

<!-- [SECTION: TEMPLATE] -->
<div class="repository-dashboard-grid">
  <DashboardDataGrid
    data={preFilteredData}
    {columns}
    selectable
    bind:selectedIds
    sortable
    bind:sortColumn
    bind:sortDirection
    filterable
    bind:filterText
    paginated
    bind:page={currentPage}
    pageSize={20}
    filterPlaceholder={$t.dashboard?.search}
    actions={rowActions}
    loading={bulkActionRunning}
  >
    {#snippet bulkActions()}
      {#if selectedIds.length > 0}
        <div class="flex flex-wrap items-center gap-2 rounded-lg border border-primary-ring bg-primary-light/60 px-3 py-2">
          {#if !repositoriesOnly}
            <Button size="sm" variant="secondary" onclick={handleManageSelected}
              disabled={bulkActionRunning || selectedIds.length !== 1}>
              {$t.git?.manage_selected}
            </Button>
            <Button size="sm" variant="secondary" onclick={handleInitializeRepositories}
              disabled={bulkActionRunning || selectedIds.length !== 1}>
              {$t.git?.init_repo || "Init Git"}
            </Button>
          {/if}
          <Button size="sm" variant="secondary" onclick={handleBulkSync} disabled={bulkActionRunning}>
            {$t.git?.bulk_sync}
          </Button>
          {#if !repositoriesOnly}
            <Button size="sm" variant="secondary" onclick={handleBulkCommit} disabled={bulkActionRunning}>
              {$t.git?.bulk_commit}
            </Button>
          {/if}
          <Button size="sm" variant="secondary" onclick={handleBulkPull} disabled={bulkActionRunning}>
            {$t.git?.bulk_pull}
          </Button>
          <Button size="sm" variant="secondary" onclick={handleBulkPush} disabled={bulkActionRunning}>
            {$t.git?.bulk_push}
          </Button>
          {#if repositoriesOnly}
            <Button size="sm" variant="destructive" onclick={() => showBulkDeleteConfirm = true} disabled={bulkActionRunning}>
              {$t.git?.delete_repo || "Delete"}
            </Button>
          {/if}
          <span class="ml-1 text-xs font-medium text-text-muted">
            {$t.git?.selected_count.replace("{count}", String(selectedIds.length))}
          </span>
        </div>
      {/if}
    {/snippet}
  </DashboardDataGrid>
</div>

{#if showGitManager && gitDashboardId}
  <GitManager
    dashboardId={gitDashboardId}
    envId={envId}
    dashboardTitle={gitDashboardTitle}
    bind:show={showGitManager}
  />
{/if}

<ConfirmDialog
  bind:show={showBulkDeleteConfirm}
  title="Delete repositories?"
  message={$t.git?.confirm_delete_repo || "Delete selected repositories?"}
  variant="destructive"
  confirmLabel="Delete"
  cancelLabel="Cancel"
  onConfirm={onConfirmBulkDelete}
  onCancel={() => {}}
/>

<!-- #endregion Dashboard.RepositoryGrid -->
