/* process-history.js
   - Prefer run_id as Batch ID (per-row) when present
   - Support 'all' category to show true statuses (All = Processed ∪ Suspect ∪ Rejected)
   - When 'received' is selected: show ALL files but status column displays "RECEIVED"
   - When 'delivered' selected: show SUCCESS rows but status column displays "DELIVERED"
   - When 'processed' selected: show SUCCESS rows without any suspect flags and display "PROCESSED"
   - When 'rejected' selected: show rejected/duplicate rows and display "REJECTED"
   - When 'suspect' selected: show rows that have at least one suspected field (heuristic), excluding duplicates/rejected
*/

console.debug("[process-history.js] status filter + frontend batch (run_id-aware)");

/* ---------- Modal-friendly warning (falls back to alert) ---------- */
function showWarning(message) {
  try {
    if (typeof $ === "function" && $("#dateWarningModal").length) {
      $("#dateWarningModal .modal-body").text(message || "Please check your inputs.");
      $("#dateWarningModal").modal("show");
      return;
    }
  } catch (e) {}
  alert(message || "Please check your inputs.");
}

/* ---------- Date formatting helpers ---------- */
function formatToMMDDYYYYTS(value) {
  if (!value) return "";
  const s = String(value).trim();
  const m = s.match(/^(\d{4})-(\d{2})-(\d{2})(.*)$/);
  if (!m) return s;
  const yyyy = m[1], mm = m[2], dd = m[3], rest = m[4] || "";
  return `${mm}-${dd}-${yyyy}${rest}`;
}
function formatYMDToMMDDYYYY(ymd) {
  const m = (ymd || "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
  return m ? `${m[2]}${m[3]}${m[1]}` : "";
}

/* ---------- Status prettifier (map SUCCESS -> PROCESSED; FAILED -> REJECTED) ---------- */
function prettyStatus(v) {
  const s = (v ?? "").toString().trim();
  if (!s) return "";
  const up = s.toUpperCase();
  if (up === "SUCCESS") return "PROCESSED";
  if (up === "FAILED") return "REJECTED";
  return s;
}

/* ---------- Query-string helpers ---------- */
function getQuery() {
  const u = new URL(window.location.href);
  return {
    state: u.searchParams.get("state") || "",
    from: u.searchParams.get("from") || "",
    to: u.searchParams.get("to") || "",
    category: (u.searchParams.get("category") || "").toLowerCase()
  };
}
function isValidCategory(c) {
  return ["all","received","processed","delivered","rejected","suspect"].includes((c||"").toLowerCase());
}

/* ---------- Filters storage ---------- */
function getSavedFilters() {
  try { return JSON.parse(localStorage.getItem("historyFilters") || "{}"); }
  catch { return {}; }
}
function saveFilters(f) {
  localStorage.setItem("historyFilters", JSON.stringify(f || {}));
}
function setAppliedFlag(on) {
  if (on) localStorage.setItem("historyFiltersApplied", JSON.stringify({ applied: true, ts: Date.now() }));
  else localStorage.removeItem("historyFiltersApplied");
}
function isApplied() {
  try { return !!JSON.parse(localStorage.getItem("historyFiltersApplied") || "{}").applied; }
  catch { return false; }
}
function getUIFilters() {
  return {
    category: document.getElementById("categorySelect")?.value || "received",
    fromDate: document.getElementById("fromDate")?.value || "",
    toDate: document.getElementById("toDate")?.value || "",
    state: document.getElementById("stateSelect")?.value || ""
  };
}
function applyFiltersToUI() {
  const f = getSavedFilters();
  if (document.getElementById("categorySelect")) document.getElementById("categorySelect").value = f.category || "received";
  if (document.getElementById("fromDate")) document.getElementById("fromDate").value = f.fromDate || "";
  if (document.getElementById("toDate")) document.getElementById("toDate").value = f.toDate || "";
  if (document.getElementById("stateSelect")) document.getElementById("stateSelect").value = f.state || "";
}
function allFiltersSet(f) { return !!(f.fromDate && f.toDate && f.state); }

/* Accept filters pushed from dashboard via URL */
function syncFromQueryToStorage() {
  const q = getQuery();
  const f = getSavedFilters();

  const merged = {
    category: isValidCategory(q.category) ? q.category : (f.category || "received"),
    fromDate: q.from || f.fromDate || "",
    toDate: q.to || f.toDate || "",
    state: q.state || f.state || ""
  };

  saveFilters(merged);

  if (merged.state && merged.fromDate && merged.toDate) {
    setAppliedFlag(true);
  }
}

/* ---------- Table helpers ---------- */
function getTbody() {
  return document.getElementById("filesTbody")
      || document.querySelector(".table tbody")
      || document.querySelector("tbody");
}
function clearTableWithMessage(msg) {
  const tb = getTbody();
  if (!tb) return;
  tb.innerHTML = `<tr><td colspan="6" class="text-center text-muted">${msg}</td></tr>`;
}

/* ---------- Batch ID computation (prefer run_id per-row) ---------- */
function computeBatchIDs(rows, gapMin = 30) {
  const gapMs = gapMin * 60 * 1000;
  const byDate = new Map();
  for (const r of rows) {
    const d = r.date || "";
    if (!byDate.has(d)) byDate.set(d, []);
    byDate.get(d).push(r);
  }

  const result = new Map();
  for (const [date, items] of byDate.entries()) {
    const mmddyyyy = formatYMDToMMDDYYYY(date);

    // Sort for deterministic grouping when run_id not present
    items.sort((a, b) => {
      const as = Date.parse(a.started || "") || Date.parse(a.ended || "") || 0;
      const bs = Date.parse(b.started || "") || Date.parse(b.ended || "") || 0;
      if (as !== bs) return as - bs;
      const af = (a.filename || "").toLowerCase();
      const bf = (b.filename || "").toLowerCase();
      if (af < bf) return -1;
      if (af > bf) return 1;
      return 0;
    });

    // compute time-based batch numbers for the date
    let batchNo = 1;
    let lastStart = null;

    for (const r of items) {
      const key = `${date}||${r.filename || ""}`;

      // If row provides a run_id, prefer that (per-row)
      if (r && r.run_id) {
        result.set(key, String(r.run_id));
        // also update lastStart so grouping of non-run rows continues sensibly
        const sTs = Date.parse(r.started || "") || 0;
        if (sTs) lastStart = sTs;
        continue;
      }

      // Otherwise assign computed MMDDYYYY-### label
      const startTs = Date.parse(r.started || "") || 0;
      if (lastStart != null && startTs && (startTs - lastStart) > gapMs) batchNo += 1;
      if (startTs) lastStart = startTs;

      const label = mmddyyyy ? `${mmddyyyy}-${String(batchNo).padStart(3,"0")}` : "";
      result.set(key, label || `${mmddyyyy}-001`);
    }
  }
  return result;
}

/* ---------- Suspect detection helper (heuristic)
   Checks for common shapes of suspect metadata that may be returned by the API:
   - r.suspect_map  (object)
   - r.suspect_values (object)
   - r.suspect_count / r.suspect_cnt / r.suspects (number)
   - r.is_suspect (1/true)
   - r.suspect (boolean/string)
   - Any field name containing "suspect" with truthy value
   This is intentionally permissive so frontend works with whichever backend change you apply.
*/
function hasSuspect(r) {
  if (!r) return false;

  // direct flags / fields
  if (r.suspect_map && typeof r.suspect_map === "object" && Object.keys(r.suspect_map).length) return true;
  if (r.suspect_values && typeof r.suspect_values === "object" && Object.keys(r.suspect_values).length) return true;
  if (Number(r.suspect_count) > 0 || Number(r.suspect_cnt) > 0) return true;
  if (r.is_suspect === 1 || r.is_suspect === true) return true;
  if (typeof r.suspect === "boolean" && r.suspect) return true;
  if (typeof r.suspect === "number" && r.suspect > 0) return true;
  if (typeof r.suspect === "string" && r.suspect.toLowerCase() === "true") return true;

  // fallback: sometimes stored under 'rejection_reason' or 'rejected_for' with "suspect" mention
  if (typeof r.rejection_reason === "string" && r.rejection_reason.toLowerCase().includes("suspect")) return true;
  if (typeof r.rejected_for === "string" && r.rejected_for.toLowerCase().includes("suspect")) return true;

  // any key that contains 'suspect' and has a truthy value
  for (const k of Object.keys(r)) {
    if (k.toLowerCase().includes("suspect")) {
      const v = r[k];
      if (v && (typeof v === "object" ? Object.keys(v).length > 0 : String(v).trim() !== "")) return true;
    }
  }

  return false;
}

/* ---------- Duplicate / Rejected detection helper ----------
   Treat as rejected if:
     - status is FAILED or DUPLICATE
     - OR rejected_for/rejection_reason present
     - OR is_duplicate flag is truthy (1/true)
*/
function isRejectedOrDuplicate(r) {
  if (!r) return false;
  const s = (r.status || "").toString().toUpperCase();
  if (s === "FAILED" || s === "DUPLICATE") return true;
  if (r.is_duplicate === 1 || r.is_duplicate === true) return true;
  if (typeof r.rejected_for === "string" && r.rejected_for.trim() !== "") return true;
  if (typeof r.rejection_reason === "string" && r.rejection_reason.trim() !== "") return true;
  return false;
}

/* ---------- Rendering with Batch ID ---------- */
function renderRows(items, displayCategory) {
  const tb = getTbody();
  if (!tb) return;

  if (!items || !items.length) {
    clearTableWithMessage("No records to display for the selected filters.");
    return;
  }

  const batchMap = computeBatchIDs(items, 30);

  const rows = items.map(r => {
    const dateYMD = r.date || "";
    const processDate = formatToMMDDYYYYTS(dateYMD);
    const batchID = (r && r.run_id) ? String(r.run_id) : (batchMap.get(`${dateYMD}||${r.filename || ""}`) || "");
    const filename = r.filename || "";

    // Determine displayed status text according to selected category AND file metadata
    const origStatus = (r && r.status) ? String(r.status) : "";
    const rejected = isRejectedOrDuplicate(r);
    const suspect = hasSuspect(r);
    let statusText;

    if (displayCategory === "all") {
      // All shows union of Processed, Suspect, Rejected.
      // Decide display label per-row: Rejected > Suspect > Processed
      if (rejected) statusText = "REJECTED";
      else if (suspect) statusText = "SUSPECT";
      else if (String(origStatus).toUpperCase() === "SUCCESS") statusText = "PROCESSED";
      else statusText = prettyStatus(origStatus);
    } else if (displayCategory === "received") {
      statusText = "RECEIVED";
    } else if (displayCategory === "delivered") {
      statusText = "DELIVERED";
    } else if (displayCategory === "processed") {
      statusText = "PROCESSED";
    } else if (displayCategory === "rejected") {
      statusText = "REJECTED";
    } else if (displayCategory === "suspect") {
      statusText = "SUSPECT";
    } else {
      statusText = prettyStatus(origStatus);
    }

    const guid = r.guid || "";
    const viewHref = (dateYMD && filename)
      ? `view.html?date=${encodeURIComponent(dateYMD)}&filename=${encodeURIComponent(filename)}`
      : `#`;

    return `<tr>
      <td>${processDate}</td>
      <td>${escapeHtml(batchID)}</td>
      <td>${escapeHtml(filename || "-")}</td>
      <td>${escapeHtml(statusText)}</td>
      <td>${escapeHtml(guid)}</td>
      <td><a href="${viewHref}" class="btn btn-sm btn-primary"${viewHref==="#"?' tabindex="-1" aria-disabled="true"':''}>View</a></td>
    </tr>`;
  });

  tb.innerHTML = rows.join("");
}

/* small helper escape */
function escapeHtml(s){
  if (s === undefined || s === null) return "";
  return String(s)
    .replace(/&/g,"&amp;")
    .replace(/</g,"&lt;")
    .replace(/>/g,"&gt;")
    .replace(/"/g,"&quot;")
    .replace(/'/g,"&#39;");
}

/* ---------- Fetch + category-filter + render ---------- */
async function fetchAndRender() {
  const f = getUIFilters();
  if (!allFiltersSet(f) || !isApplied()) {
    clearTableWithMessage("Select a date range, then click Apply.");
    return;
  }

  const params = new URLSearchParams({ from: f.fromDate, to: f.toDate, state: f.state });

  try {
    const res = await fetch(`/api/files?${params.toString()}`, { credentials: "include" });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const data = await res.json();

    const cat = (f.category || "received").toLowerCase();
    let filtered = Array.isArray(data) ? data.slice() : [];

    if (cat === "processed") {
      // processed: SUCCESS rows that have NO suspects and are not rejected/duplicate
      filtered = filtered.filter(r => {
        const s = String(r.status || "").toUpperCase();
        if (s !== "SUCCESS") return false;
        if (isRejectedOrDuplicate(r)) return false;
        if (hasSuspect(r)) return false;
        return true;
      });
    } else if (cat === "delivered") {
      // same as processed per your rules
      filtered = filtered.filter(r => {
        const s = String(r.status || "").toUpperCase();
        if (s !== "SUCCESS") return false;
        if (isRejectedOrDuplicate(r)) return false;
        if (hasSuspect(r)) return false;
        return true;
      });
    } else if (cat === "rejected") {
      filtered = filtered.filter(r => {
        // Keep existing rejected semantics: FAILED, DUPLICATE, or explicit rejection metadata
        return isRejectedOrDuplicate(r);
      });
    } else if (cat === "suspect") {
      // Show rows with any suspected field BUT exclude duplicates/rejected
      filtered = filtered.filter(r => hasSuspect(r) && !isRejectedOrDuplicate(r));
    } else if (cat === "received") {
      // Received: show all rows (no extra filter)
      filtered = filtered.slice();
    } else if (cat === "all") {
      // All Status = union of Processed, Suspect, Rejected
      // Build union by filtering rows that satisfy any of the three predicates.
      filtered = filtered.filter(r => {
        if (isRejectedOrDuplicate(r)) return true;
        if (hasSuspect(r) && !isRejectedOrDuplicate(r)) return true;
        const s = String(r.status || "").toUpperCase();
        if (s === "SUCCESS" && !hasSuspect(r) && !isRejectedOrDuplicate(r)) return true;
        return false;
      });
    }

    renderRows(filtered, cat);
  } catch (e) {
    console.error("Process History fetch error:", e);
    clearTableWithMessage("Error loading data. Please adjust filters or try again.");
  }
}

/* ---------- Init ---------- */
document.addEventListener("DOMContentLoaded", () => {
  syncFromQueryToStorage();
  applyFiltersToUI();

  if (isApplied() && allFiltersSet(getUIFilters())) fetchAndRender();
  else clearTableWithMessage("Select a date range, then click Apply.");

  const applyBtn = document.getElementById("applyFilters");
  if (applyBtn) {
    applyBtn.addEventListener("click", () => {
      const f = getUIFilters();
      const onlyOneDate = (!!f.fromDate && !f.toDate) || (!f.fromDate && !!f.toDate);
      if (onlyOneDate) {
        showWarning("Please select both Start and End date.");
        return;
      }
      if (!allFiltersSet(f)) {
        clearTableWithMessage("Select a date range, then click Apply.");
        setAppliedFlag(false);
        return;
      }
      saveFilters(f);
      setAppliedFlag(true);
      fetchAndRender();
    });
  }

  const clearBtn = document.getElementById("clearFilters");
  if (clearBtn) {
    clearBtn.addEventListener("click", () => {
      localStorage.removeItem("historyFilters");
      setAppliedFlag(false);
      const c = document.getElementById("categorySelect");
      const s = document.getElementById("stateSelect");
      const fd = document.getElementById("fromDate");
      const td = document.getElementById("toDate");
      if (c) c.value = "received";
      if (s) s.value = "";
      if (fd) fd.value = "";
      if (td) td.value = "";
      clearTableWithMessage("Select a date range, then click Apply.");
    });
  }

  window.addEventListener("storage", (event) => {
    if (event.key === "historyFilters") {
      applyFiltersToUI();
      if (isApplied() && allFiltersSet(getUIFilters())) fetchAndRender();
    }
    if (event.key === "historyFiltersApplied") {
      if (isApplied() && allFiltersSet(getUIFilters())) { applyFiltersToUI(); fetchAndRender(); }
      else { clearTableWithMessage("Select a date range, then click Apply."); }
    }
  });
});
