// ─────────────────────────────────────────────
// DO_ListView.jsx
// List view driven by group:app type:listview object
// Maintains found set — full ordered list of r_auto
// values passed to detail view for Next/Prev nav
// ─────────────────────────────────────────────

const { useState, useEffect, useCallback, useRef } = React;

function getListViewConfig(allObjects, table) {
  const defined = allObjects.find(
    o => o.group === "app" && o.type === "listview" && o.table === table
  );
  if (defined) return defined;

  // No listview object — build default from first 6 schema fields
  const schemaFields = allObjects
    .filter(o => o.group === "schema" && o.type === "field" && o.table === table && o.status !== "0")
    .sort((a, b) => {
      const na = parseInt((a.field || "").replace(/[^0-9]/g,"")) || 0;
      const nb = parseInt((b.field || "").replace(/[^0-9]/g,"")) || 0;
      return na - nb;
    })
    .slice(0, 6);

  if (schemaFields.length === 0) return null;

  // Get table name from schema
  const tableObj = allObjects.find(o => o.group === "schema" && o.type === "table" && o.table === table);

  return {
    table,
    label:     tableObj?.name || table,
    pagesize:  25,
    sortfield: "r_auto",
    sortdir:   "asc",
    fields:    schemaFields.map(f => ({
      field: f.field,
      label: f.name || f.label || f.field,
      width: 2,
    }))
  };
}

const DOListView = React.forwardRef(function DOListView(
  { allObjects, table, searchOpen, onSearchClose, onOpen, onFoundSetChange, onTotalRecords, theme = {} },
  ref
) {
  const foundSetRef = React.useRef([]);  // always holds latest found set

  // Expose methods to parent via ref
  React.useImperativeHandle(ref, () => ({
    refresh: () => fetchPage(offset, sort, dir, activeSearch),
    goNext:  () => goToPage(currentPage + 1),
    goPrev:  () => goToPage(currentPage - 1),
  }));
  const cfg = getListViewConfig(allObjects, table);

  const fields   = cfg?.fields   || [{ field: "f1", label: "Name", width: 4 }];
  const pageSize = parseInt(cfg?.pagesize) || 25;
  const defSort  = cfg?.sortfield || "r_auto";
  const defDir   = cfg?.sortdir   || "asc";
  const label    = cfg?.label     || table;

  const [records,     setRecords]     = useState([]);
  const [total,       setTotal]       = useState(0);
  const [offset,      setOffset]      = useState(0);
  const [sort,        setSort]        = useState(defSort);
  const [dir,         setDir]         = useState(defDir);
  const [loading,     setLoading]     = useState(true);
  const [error,       setError]       = useState(null);
  const [apiLog,      setApiLog]      = useState([]);

  // Search state
  // searchOpen controlled by nav bar via prop
  const [searchGlobal, setSearchGlobal] = useState("");
  const [searchField,  setSearchField]  = useState("");
  const [searchOp,     setSearchOp]     = useState("contains");
  const [searchValue,  setSearchValue]  = useState("");
  const [activeSearch, setActiveSearch] = useState(null);

  const log = (msg) =>
    setApiLog(prev => [`${new Date().toLocaleTimeString()} — ${msg}`, ...prev].slice(0, 8));

  const fieldList = fields.map(f => f.field).join(",");

  // ─────────────────────────────────────────────
  // FETCH PAGE — display records for current page
  // ─────────────────────────────────────────────
  const fetchPage = useCallback(async (newOffset, newSort, newDir, search) => {
    setLoading(true);
    setError(null);

    const params = new URLSearchParams({
      app:    APP_ID,
      table,
      fields: fieldList,
      limit:  pageSize,
      offset: newOffset,
      sort:   newSort,
      dir:    newDir,
    });

    if (search?.type === "global") {
      params.set("search", search.term);
      params.set("searchfields", fieldList);  // search all listview fields
    }
    if (search?.type === "field") {
      params.set("field", search.field);
      params.set("op",    search.op);
      params.set("value", search.value);
    }

    log(`Fetching page — offset=${newOffset} sort=${newSort} ${newDir}`);

    try {
      const res  = await apiFetch(`${API_BASE}/v6/records?${params}&_t=${Date.now()}`);
      const data = await res.json();
      if (data.status !== "ok") throw new Error(data.message);

      setRecords(data.records);
      setTotal(data.total);
      setOffset(newOffset);
      setSort(newSort);
      setDir(newDir);
      onTotalRecords && onTotalRecords(data.total);
      log(`✅ ${data.count} of ${data.total} records`);

      // ── Fetch full found set (all r_auto values) ──
      // Used by detail view for Next/Prev navigation
      // Fetch separately with limit=9999 r_auto only
      fetchFoundSet(newSort, newDir, search, data.total);

    } catch(err) {
      setError(err.message);
      log(`❌ ${err.message}`);
    } finally {
      setLoading(false);
    }
  }, [table, fieldList, pageSize]);

  // ─────────────────────────────────────────────
  // FETCH FOUND SET
  // Gets ALL r_auto values in current sort order
  // Passed up to App.jsx → down to detail view
  // ─────────────────────────────────────────────
  const fetchFoundSet = useCallback(async (newSort, newDir, search, total) => {
    if (!total || total === 0) {
      onFoundSetChange && onFoundSetChange([], 0, { sort: newSort, dir: newDir, search });
      return;
    }

    // Only fetch r_auto — minimal data transfer
    const params = new URLSearchParams({
      app:    APP_ID,
      table,
      fields: "r_auto",
      limit:  Math.min(total, 5000), // cap at 5000 for now
      offset: 0,
      sort:   newSort,
      dir:    newDir,
    });

    if (search?.type === "global") {
      params.set("search", search.term);
      params.set("searchfields", fieldList);
    }
    if (search?.type === "field") {
      params.set("field", search.field);
      params.set("op",    search.op);
      params.set("value", search.value);
    }

    try {
      const res  = await apiFetch(`${API_BASE}/v6/records?${params}`);
      const data = await res.json();
      if (data.status !== "ok") return;

      const ids = data.records.map(r => r.r_auto);
      foundSetRef.current = ids;  // store immediately in ref
      log(`✅ Found set — ${ids.length} record IDs`);

      onFoundSetChange && onFoundSetChange(ids, data.total, pageSize, 0);
      onTotalRecords && onTotalRecords(data.total);
    } catch(err) {
      console.error("Found set fetch error:", err.message);
    }
  }, [table]);

  // Initial load — restore previous found params if returning from detail
  useEffect(() => {
    // Reset all state when table changes
    setActiveSearch(null);
    setSearchGlobal("");
    setSearchField("");
    setSearchValue("");
    setSearchOp("contains");
    setRecords([]);
    setTotal(0);
    setOffset(0);
    setSort(defSort);
    setDir(defDir);
    fetchPage(0, defSort, defDir, null);
  }, [table]);  // re-run whenever table changes

  // ── Sort by column ───────────────────────────
  const handleSort = (field) => {
    const newDir = sort === field && dir === "asc" ? "desc" : "asc";
    fetchPage(0, field, newDir, activeSearch);
  };

  // ── Pagination ───────────────────────────────
  const totalPages  = Math.ceil(total / pageSize);
  const currentPage = Math.floor(offset / pageSize) + 1;
  const goToPage    = (page) => fetchPage((page - 1) * pageSize, sort, dir, activeSearch);

  // ── Open record — pass current found set ─────
  const handleRowClick = (r_auto) => {
    // Pass found set directly from ref — guaranteed current even if async not done
    onOpen && onOpen(r_auto, foundSetRef.current, total);
  };

  // ── Search ───────────────────────────────────
  const handleGlobalSearch = () => {
    if (!searchGlobal.trim()) return handleClearSearch();
    const search = { type: "global", term: searchGlobal.trim() };
    setActiveSearch(search);
    onSearchClose && onSearchClose();
    fetchPage(0, sort, dir, search);
  };

  const handleFieldSearch = () => {
    if (!searchField || !searchValue.trim()) return;
    const search = { type: "field", field: searchField, op: searchOp, value: searchValue.trim() };
    setActiveSearch(search);
    onSearchClose && onSearchClose();
    fetchPage(0, sort, dir, search);
  };

  const handleClearSearch = () => {
    setActiveSearch(null);
    setSearchGlobal("");
    setSearchField("");
    setSearchValue("");
    onSearchClose && onSearchClose();
    fetchPage(0, sort, dir, null);
  };

  const colWidth = (w) => {
    const totalW = fields.reduce((s, f) => s + (parseInt(f.width) || 1), 0);
    return `${((parseInt(w) || 1) / totalW) * 85}%`;
  };

  const sortIcon = (f) => sort === f ? (dir === "asc" ? " ↑" : " ↓") : " ↕";

  const activeSearchLabel = activeSearch
    ? activeSearch.type === "global"
      ? `"${activeSearch.term}"`
      : `${fields.find(f => f.field === activeSearch.field)?.label || activeSearch.field} ${activeSearch.op} "${activeSearch.value}"`
    : null;

  return (
    <div style={SL.wrap}>

      {/* ── TOOLBAR ── */}
      <div style={SL.toolbar}>
        <div style={SL.toolbarLeft}>
          <span style={SL.tableLabel}>{label}</span>
          <span style={SL.totalBadge}>{total} records</span>
          {activeSearch && (
            <span style={SL.searchBadge}>
              🔍 {activeSearchLabel}
              <button style={SL.clearSearch} onClick={handleClearSearch}>✕</button>
            </span>
          )}
        </div>

      </div>

      {/* ── SEARCH PANEL ── */}
      {searchOpen && (
        <div style={SL.searchPanel}>
          <div style={SL.searchSection}>
            <div style={SL.searchSectionLabel}>Global Search</div>
            <div style={SL.searchRow}>
              <input
                style={SL.searchInput}
                placeholder="Search across list view fields..."
                value={searchGlobal}
                onChange={e => setSearchGlobal(e.target.value)}
                onKeyDown={e => e.key === "Enter" && handleGlobalSearch()}
              />
              <button style={SL.searchBtn} onClick={handleGlobalSearch}>Search</button>
            </div>
          </div>

          <div style={SL.searchSection} style={{marginTop: 12}}>
            <div style={SL.searchSectionLabel}>Field Search</div>
            <div style={SL.searchRow}>
              <select
                style={{ ...SL.searchInput, flex: "none", width: "auto" }}
                value={searchField}
                onChange={e => setSearchField(e.target.value)}
              >
                <option value="">— Field —</option>
                {fields.map((f, i) => (
                  <option key={i} value={f.field}>{f.label}</option>
                ))}
              </select>
              <select
                style={{ ...SL.searchInput, flex: "none", width: "auto" }}
                value={searchOp}
                onChange={e => setSearchOp(e.target.value)}
              >
                <option value="contains">contains</option>
                <option value="equals">equals</option>
                <option value="starts">starts with</option>
                <option value="ends">ends with</option>
              </select>
              <input
                style={SL.searchInput}
                placeholder="Value..."
                value={searchValue}
                onChange={e => setSearchValue(e.target.value)}
                onKeyDown={e => e.key === "Enter" && handleFieldSearch()}
              />
              <button style={SL.searchBtn} onClick={handleFieldSearch}>Search</button>
            </div>
          </div>

          {activeSearch && (
            <div style={{ paddingTop: 10 }}>
              <button style={SL.clearBtn} onClick={handleClearSearch}>✕ Clear — Show All Records</button>
            </div>
          )}
        </div>
      )}

      {/* ── ERROR ── */}
      {error && <div style={SL.errorBar}>❌ {error}</div>}

      {/* ── TABLE ── */}
      <div style={{ ...SL.tableWrap, background: theme.card_bg, borderRadius: theme.border_radius ? `${theme.border_radius}px` : undefined }}>
        <table style={SL.table}>
          <thead>
            <tr style={SL.headerRow}>
              <th style={{ ...SL.th, width: "5%" }}>#</th>
              {fields.map((f, i) => (
                <th
                  key={i}
                  style={{ ...SL.th, width: colWidth(f.width), cursor: "pointer" }}
                  onClick={() => handleSort(f.field)}
                >
                  {f.label}{sortIcon(f.field)}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {loading && (
              <tr><td colSpan={fields.length + 1} style={SL.loadingCell}>Loading...</td></tr>
            )}
            {!loading && records.length === 0 && (
              <tr><td colSpan={fields.length + 1} style={SL.emptyCell}>
                {activeSearch ? "No records match your search." : "No records found."}
              </td></tr>
            )}
            {!loading && records.map((rec, i) => (
              <tr
                key={rec.r_auto}
                style={{ ...SL.dataRow, background: i % 2 === 0 ? "#fff" : "#f8fafc" }}
                onClick={() => handleRowClick(rec.r_auto)}
                onMouseEnter={e => e.currentTarget.style.background = "#f0f7ff"}
                onMouseLeave={e => e.currentTarget.style.background = i % 2 === 0 ? "#fff" : "#f8fafc"}
              >
                <td style={{ ...SL.td, ...SL.tdNum }}>{offset + i + 1}</td>
                {fields.map((f, j) => (
                  <td key={j} style={SL.td}>{formatCell(rec[f.field])}</td>
                ))}
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* ── PAGINATION ── */}
      {total > pageSize && (
        <div style={SL.pagination}>
          <div style={SL.pageInfo}>
            Showing {offset + 1}–{Math.min(offset + pageSize, total)} of {total}
          </div>
          <div style={SL.pageButtons}>
            <button style={SL.pageBtn} disabled={currentPage === 1} onClick={() => goToPage(1)}>«</button>
            <button style={SL.pageBtn} disabled={currentPage === 1} onClick={() => goToPage(currentPage - 1)}>‹</button>
            {Array.from({ length: totalPages }, (_, i) => i + 1)
              .filter(p => p === 1 || p === totalPages || Math.abs(p - currentPage) <= 2)
              .reduce((acc, p, i, arr) => {
                if (i > 0 && p - arr[i-1] > 1) acc.push("...");
                acc.push(p);
                return acc;
              }, [])
              .map((p, i) => p === "..." ? (
                <span key={`e${i}`} style={SL.pageEllipsis}>…</span>
              ) : (
                <button
                  key={p}
                  style={{ ...SL.pageBtn, ...(p === currentPage ? SL.pageBtnActive : {}) }}
                  onClick={() => goToPage(p)}
                >{p}</button>
              ))
            }
            <button style={SL.pageBtn} disabled={currentPage === totalPages} onClick={() => goToPage(currentPage + 1)}>›</button>
            <button style={SL.pageBtn} disabled={currentPage === totalPages} onClick={() => goToPage(totalPages)}>»</button>
          </div>
        </div>
      )}

      {/* ── API LOG ── */}
      <details style={SL.logPanel}>
        <summary style={SL.logSummary}>🔌 API Log</summary>
        <div style={SL.logBody}>
          {apiLog.map((entry, i) => (
            <div key={i} style={SL.logEntry}>{entry}</div>
          ))}
        </div>
      </details>
    </div>
  );
});

function formatCell(val) {
  if (val === null || val === undefined || val === "") return React.createElement('span', { style: { color: "#cbd5e1" } }, "—");
  if (Array.isArray(val)) return val.join(", ");
  const str = String(val);
  if (str.length > 60) return str.substring(0, 60) + "…";
  return str;
}

// ─────────────────────────────────────────────
// STYLES
// ─────────────────────────────────────────────
const SL = {
  wrap:              { fontFamily: "'Segoe UI', sans-serif", fontSize: "14px", color: "#1e293b" },
  toolbar:           { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0", marginBottom: 4 },
  toolbarLeft:       { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" },
  toolbarRight:      { display: "flex", gap: 8 },
  tableLabel:        { fontSize: 16, fontWeight: 700, color: "#1a3a5c" },
  totalBadge:        { fontSize: 11, background: "#e2e8f0", color: "#64748b", padding: "2px 10px", borderRadius: 10 },
  searchBadge:       { fontSize: 11, background: "#dbeafe", color: "#1d4ed8", padding: "2px 10px", borderRadius: 10, display: "flex", alignItems: "center", gap: 6 },
  clearSearch:       { background: "none", border: "none", cursor: "pointer", color: "#1d4ed8", fontSize: 12, padding: 0 },
  toolBtn:           { background: "#fff", border: "1px solid #e2e8f0", color: "#475569", padding: "6px 14px", borderRadius: 5, cursor: "pointer", fontSize: 12, fontFamily: "inherit" },
  toolBtnActive:     { background: "#dbeafe", borderColor: "#93c5fd", color: "#1d4ed8" },
  searchPanel:       { background: "#fff", border: "1px solid #e2e8f0", borderRadius: 8, padding: "16px 20px", marginBottom: 12 },
  searchSection:     { marginBottom: 8 },
  searchSectionLabel:{ fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.07em", color: "#64748b", marginBottom: 6 },
  searchRow:         { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" },
  searchInput:       { border: "1px solid #e2e8f0", borderRadius: 5, padding: "6px 10px", fontSize: 13, fontFamily: "inherit", flex: 1, minWidth: 120, outline: "none" },
  searchBtn:         { background: "#1a3a5c", color: "#fff", border: "none", borderRadius: 5, padding: "6px 16px", cursor: "pointer", fontSize: 13, fontFamily: "inherit", whiteSpace: "nowrap" },
  searchDivider:     { textAlign: "center", color: "#94a3b8", fontSize: 12, padding: "8px 0" },
  clearBtn:          { background: "none", border: "1px solid #e2e8f0", borderRadius: 5, padding: "5px 14px", cursor: "pointer", fontSize: 12, color: "#64748b", fontFamily: "inherit" },
  errorBar:          { background: "#fee2e2", color: "#991b1b", fontSize: 12, padding: "8px 16px", borderRadius: 6, marginBottom: 8 },
  tableWrap:         { border: "1px solid #e2e8f0", borderRadius: 8, overflow: "hidden" },
  table:             { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" },
  headerRow:         { background: "#1a3a5c" },
  th:                { padding: "10px 12px", textAlign: "left", fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.07em", color: "rgba(255,255,255,0.85)", userSelect: "none", whiteSpace: "nowrap", overflow: "hidden" },
  dataRow:           { cursor: "pointer", transition: "background 0.1s", borderBottom: "1px solid #f1f5f9" },
  td:                { padding: "9px 12px", fontSize: 13, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
  tdNum:             { color: "#94a3b8", fontSize: 11, width: "5%" },
  loadingCell:       { padding: "40px", textAlign: "center", color: "#94a3b8", fontSize: 13 },
  emptyCell:         { padding: "40px", textAlign: "center", color: "#94a3b8", fontSize: 13 },
  pagination:        { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 4px" },
  pageInfo:          { fontSize: 12, color: "#64748b" },
  pageButtons:       { display: "flex", gap: 4, alignItems: "center" },
  pageBtn:           { background: "#fff", border: "1px solid #e2e8f0", color: "#475569", padding: "5px 10px", borderRadius: 4, cursor: "pointer", fontSize: 12, fontFamily: "inherit", minWidth: 32 },
  pageBtnActive:     { background: "#1a3a5c", borderColor: "#1a3a5c", color: "#fff" },
  pageEllipsis:      { padding: "0 4px", color: "#94a3b8" },
  logPanel:          { marginTop: 16, background: "#0f172a", borderRadius: 8, overflow: "hidden" },
  logSummary:        { color: "#64748b", fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.08em", padding: "10px 16px", cursor: "pointer" },
  logBody:           { padding: "0 16px 16px" },
  logEntry:          { color: "#7dd3fc", fontSize: 11, lineHeight: 1.8, borderBottom: "1px solid #1e293b", padding: "2px 0" },
};
