// #region Dashboards.FiltersModel [C:4] [TYPE Model] [SEMANTICS dashboard,filter,pagination,sort,search]
// @ingroup Dashboards
// @BRIEF Filter, sort, search, and pagination state for the dashboard hub grid. Owned by Dashboards.HubModel.
// @INVARIANT Changing filter (column, multi-select) resets pagination to page 1.
// @INVARIANT Changing search resets pagination to page 1.
// @STATE idle     — No search or filter active.
// @STATE active   — Filter or search active, grid filtered.
// @ACTION setPage(n)                    — Navigates to page n, triggers loadDashboards.
// @ACTION handleSort(column)           — Toggles sort direction or changes column.
// @ACTION toggleFilterValue(col,v,chk) — Adds/removes a column filter value.
// @ACTION clearColumnFilter(col)       — Resets column filter.
// @ACTION selectAllColumnFilterValues(col) — Selects all visible options for column.
// @ACTION updateColumnFilterSearch(col,v)  — Sets search text for column filter popover.
// @ACTION hasColumnFilter(col)         — Whether column has active filters.
// @ACTION applyGridTransforms(all, serverTotal, serverTotalPages) — Computes sorted/filtered dashboards.
// @RELATION CALLED_BY -> [Dashboards.HubModel]

import { SvelteSet } from "svelte/reactivity";
import { t } from "$lib/i18n/index.svelte.js";

// ── Types ────────────────────────────────────────────────────────

export type SortDir = "asc" | "desc";
export type FilterColumn = "title" | "git_status" | "validation_status" | "changed_on" | "actor";
export type ColumnFilters = Record<FilterColumn, Set<string>>;
export type ColumnFilterSearch = Record<FilterColumn, string>;

export interface DashboardRow {
  id: string; title: string; slug: string;
  changedOn: string | null; changedOnLabel: string;
  owners: string[]; actorLabel: string;
  git: { status: string; branch: string | null; hasRepo: boolean | null; hasChangesForCommit: boolean };
  lastTask: { status: string | null; validationStatus: string; id: string } | null;
  actions: string[];
}

// ═══ Model ═══════════════════════════════════════════════════════

export class DashboardsFiltersModel {
  // ── Pagination ────────────────────────────────────────────────
  currentPage: number = $state(1);
  pageSize: number = $state(10);
  totalPages: number = $state(1);
  total: number = $state(0);

  // ── Search ────────────────────────────────────────────────────
  searchQuery: string = $state("");
  filterDashboardIds: string[] = $state([]);
  searchableDashboardOptions: Array<{id: string; name: string; subtitle: string; hint: string}> = $state([]);
  searchableDashboardLoading: boolean = $state(false);

  // ── Sort ──────────────────────────────────────────────────────
  sortColumn: FilterColumn = $state("title");
  sortDirection: SortDir = $state("asc");

  // ── Column filters ────────────────────────────────────────────
  openFilterColumn: FilterColumn | null = $state(null);
  filterDropdownPosition: { left: number; top: number } = $state({ left: 0, top: 0 });
  columnFilterSearch: ColumnFilterSearch = $state({ title: "", git_status: "", validation_status: "", changed_on: "", actor: "" });
  columnFilters: ColumnFilters = $state({ title: new SvelteSet(), git_status: new SvelteSet(), validation_status: new SvelteSet(), changed_on: new SvelteSet(), actor: new SvelteSet() });

  // ── Profile filter ────────────────────────────────────────────
  profileFilterOverrideShowAll: boolean = $state(false);
  effectiveProfileFilter: any = $state(null);

  // ═══ Pagination ════════════════════════════════════════════════

  setPage(page: number): void {
    if (page === this.currentPage) return;
    this.currentPage = page;
  }

  setPageSize(event: Event): void {
    this.pageSize = parseInt((event.target as HTMLSelectElement).value);
    this.currentPage = 1;
  }

  // ═══ Profile filter ════════════════════════════════════════════

  handleTemporaryShowAll(): void {
    if (this.profileFilterOverrideShowAll) return;
    this.profileFilterOverrideShowAll = true;
    this.currentPage = 1;
  }

  handleRestoreProfileFilter(): void {
    if (!this.profileFilterOverrideShowAll) return;
    this.profileFilterOverrideShowAll = false;
    this.currentPage = 1;
  }

  // ═══ Column filters ═══════════════════════════════════════════

  getValidationLabelForCell(dashboard: DashboardRow, validationStatuses: Record<string, any>): string {
    const vs = validationStatuses[dashboard.id];
    return (!vs || !vs.status) ? "unknown" : String(vs.status).toLowerCase();
  }

  getColumnCellValue(dashboard: DashboardRow, column: FilterColumn, validationStatuses: Record<string, any>): string {
    if (column === "title") return dashboard.title || "-";
    if (column === "git_status") return String(dashboard.git?.status || "no_repo").toLowerCase();
    if (column === "validation_status") return this.getValidationLabelForCell(dashboard, validationStatuses);
    if (column === "changed_on") return dashboard.changedOn ? String(dashboard.changedOn).slice(0, 10) : "-";
    if (column === "actor") return dashboard.actorLabel || "-";
    return "-";
  }

  /** Map raw git status token to human-readable i18n label for display. */
  getGitStatusLabel(dashboard: DashboardRow): string {
    const hasRepo = dashboard.git?.hasRepo;
    if (hasRepo === null) return t.common?.loading || "Loading";
    if (!hasRepo) return t.dashboard?.status_no_repo || "No Repo";
    return dashboard.git?.hasChangesForCommit
      ? (t.dashboard?.status_changes || "Diff")
      : (t.dashboard?.status_no_changes || "Synced");
  }

  /** Get display label for a raw filter value. Used by ColumnFilterPopover. */
  getFilterOptionLabel(column: FilterColumn, rawValue: string): string {
    if (column === "git_status") {
      const map: Record<string, string> = {
        "no_repo": t.dashboard?.status_no_repo || "No Repo",
        "diff": t.dashboard?.status_changes || "Diff",
        "ok": t.dashboard?.status_no_changes || "Synced",
        "error": "Error",
        "pending": "Pending",
      };
      return map[rawValue] || rawValue;
    }
    return rawValue;
  }

  getFilterOptions(column: FilterColumn, allDashboards: DashboardRow[], validationStatuses: Record<string, any>): string[] {
    return Array.from(new SvelteSet(
      allDashboards.map(d => this.getColumnCellValue(d, column, validationStatuses)).filter(Boolean)
    )).sort((a, b) => a.localeCompare(b, "ru"));
  }

  getVisibleFilterOptions(column: FilterColumn, allDashboards: DashboardRow[], validationStatuses: Record<string, any>): string[] {
    const searchText = (this.columnFilterSearch[column] || "").toLowerCase();
    return this.getFilterOptions(column, allDashboards, validationStatuses).filter(v => v.toLowerCase().includes(searchText));
  }

  toggleFilterDropdown(column: FilterColumn, event: Event | undefined, panelWidth = 256): void {
    event?.stopPropagation();
    if (this.openFilterColumn === column) { this.openFilterColumn = null; return; }
    const trigger = (event as any)?.currentTarget;
    if (trigger?.getBoundingClientRect) {
      const rect = trigger.getBoundingClientRect();
      const vw = typeof window !== "undefined" ? window.innerWidth : 1920;
      this.filterDropdownPosition = { left: Math.max(8, Math.min(rect.left, vw - panelWidth - 8)), top: rect.bottom + 8 };
    }
    this.openFilterColumn = column;
  }

  toggleFilterValue(column: FilterColumn, value: string, checked: boolean): void {
    const next = new SvelteSet(this.columnFilters[column]);
    checked ? next.add(value) : next.delete(value);
    this.columnFilters = { ...this.columnFilters, [column]: next };
    this.currentPage = 1;
  }

  clearColumnFilter(column: FilterColumn): void {
    this.columnFilters = { ...this.columnFilters, [column]: new SvelteSet() };
    this.currentPage = 1;
  }

  selectAllColumnFilterValues(column: FilterColumn, allDashboards: DashboardRow[], validationStatuses: Record<string, any>): void {
    this.columnFilters = { ...this.columnFilters, [column]: new SvelteSet(this.getVisibleFilterOptions(column, allDashboards, validationStatuses)) };
    this.currentPage = 1;
  }

  updateColumnFilterSearch(column: FilterColumn, value: string): void {
    this.columnFilterSearch = { ...this.columnFilterSearch, [column]: value };
  }

  hasColumnFilter(column: FilterColumn): boolean { return this.columnFilters[column]?.size > 0; }

  // ═══ Sort ══════════════════════════════════════════════════════

  getSortValue(dashboard: DashboardRow, column: FilterColumn, gitSummary: string): string | number {
    if (column === "title") return (dashboard.title || "").toLowerCase();
    if (column === "git_status") return gitSummary.toLowerCase();
    if (column === "validation_status") return (this.getValidationLabelForCell(dashboard, {}) || "").toLowerCase();
    if (column === "changed_on") return dashboard.changedOn ? new Date(dashboard.changedOn).getTime() : 0;
    if (column === "actor") return (dashboard.actorLabel || "").toLowerCase();
    return "";
  }

  /** Returns a lightweight label for git status (for sort computation). */
  getSortGitLabel(dashboard: DashboardRow): string {
    if (dashboard.git?.hasRepo === null) return "loading";
    if (!dashboard.git?.hasRepo) return "no repo";
    return dashboard.git?.hasChangesForCommit ? "diff" : "synced";
  }

  handleSort(column: FilterColumn): void {
    if (this.sortColumn === column) {
      this.sortDirection = this.sortDirection === "asc" ? "desc" : "asc";
    } else {
      this.sortColumn = column;
      this.sortDirection = "asc";
    }
    this.currentPage = 1;
  }

  // ═══ Grid transforms ═══════════════════════════════════════════

  /**
   * Applies filter-by-id, sort to allDashboards. Returns the transformed arrays and totals.
   * Does NOT mutate external state — the caller (HubModel) handles cross-cutting updates.
   */
  applyGridTransforms(allDashboards: DashboardRow[], serverTotal: number, serverTotalPages: number): { filteredDashboards: DashboardRow[]; dashboards: DashboardRow[]; total: number; totalPages: number } {
    let next = allDashboards;
    if (this.filterDashboardIds.length > 0) {
      next = next.filter(d => this.filterDashboardIds.includes(d.id));
    }
    next = [...next].sort((a, b) => {
      const gitALabel = this.getSortGitLabel(a);
      const gitBLabel = this.getSortGitLabel(b);
      const aV = this.getSortValue(a, this.sortColumn, gitALabel);
      const bV = this.getSortValue(b, this.sortColumn, gitBLabel);
      if (aV < bV) return this.sortDirection === "asc" ? -1 : 1;
      if (aV > bV) return this.sortDirection === "asc" ? 1 : -1;
      return 0;
    });
    return {
      filteredDashboards: next,
      dashboards: next,
      total: serverTotal,
      totalPages: serverTotalPages,
    };
  }
}
// #endregion Dashboards.FiltersModel
