import { AUTOCORRECTED_FIELDS_LS, EVENT_TYPE_OPTIONS, HEADER_EVENT_TYPE_OPTIONS } from '../../config/constants.js';
import { $, escapeHtml } from '../../core/dom.js';
import { readLs } from '../../core/storage.js';
import { normalizeUiText, normalizeUiTree } from '../../core/text.js';
import { getConfiguredFieldContextForRecord, getCurrentProjectKind, getVisibleFieldBases, isActiveWorkNonDataDocType, isEventTypeFieldBase, isHeaderKeyOnlyDocType, isHiddenField, isOrigBasePersistable, normalizeEventTypeValue, normalizeHeaderEventTypeValue, resolveFieldConfigRootKind, resolveFieldLabel, resolveFieldMeta, resolveFieldRekeyThreshold } from '../../services/project-context.js';
import { state } from '../../state/app-state.js';
import { filterEmptyOnly, findMoveTargetIndex, formContent, formatConfPercentDisplay, parseConf, recordsBlock, toRatio } from '../events.js';

export function fieldLowConf(meta, confVal, base) {
  const th = resolveFieldRekeyThreshold(base, meta);
  if (th == null) return false;
  const thresholdVal = parseConf(th);
  if (thresholdVal === null) return false;
  const thresholdRatio = toRatio(thresholdVal);
  if (thresholdRatio === null) return false;
  const c = parseConf(confVal);
  if (c === null) return false;
  const confRatio = toRatio(c);
  if (confRatio === null) return false;
  return confRatio < thresholdRatio;
}

/** HWR confidence (conf) meets Seuil Rekey for this field. */

export function fieldConfMeetsRekeyThreshold(base, confVal, meta) {
  const th = resolveFieldRekeyThreshold(base, meta);
  if (th == null) return false;
  const thresholdVal = parseConf(th);
  if (thresholdVal === null) return false;
  const thresholdRatio = toRatio(thresholdVal);
  if (thresholdRatio === null) return false;
  const c = parseConf(confVal);
  if (c === null) return false;
  const confRatio = toRatio(c);
  if (confRatio === null) return false;
  return confRatio >= thresholdRatio;
}

/** Toutes les variantes vides → rien à afficher pour ce champ. */

export function fieldAllVariantsEmpty(meta) {
  const o = (meta.orig || "").trim();
  const h = (meta.hwr || "").trim();
  const c = (meta.conf || "").trim();
  return !o && !h && !c;
}

/**
 * Whether this field was marked by Auto-correct / AI Page Fill in this browser.
 * Keys are image-scoped so RecordID reuse across images cannot paint virgin rows blue.
 */
function hasPersistedAutocorrectMarker(base, recordId, headerId) {
  if (!state.batch || !base || !state.work || state.work.image_id == null) return false;
  const entityKey = recordId != null ? `r:${recordId}` : headerId != null ? `h:${headerId}` : "";
  if (!entityKey) return false;
  const key = `${state.batch}|i:${state.work.image_id}|${entityKey}|${base}`;
  try {
    const markers = JSON.parse(readLs(AUTOCORRECTED_FIELDS_LS) || "{}") || {};
    return Object.prototype.hasOwnProperty.call(markers, key);
  } catch (_) {
    return false;
  }
}


export function buildEventTypeOrigSelect(col, val) {
  const sel = document.createElement("select");
  sel.className = "field-table__input";
  sel.dataset.col = col;
  EVENT_TYPE_OPTIONS.forEach((optLabel) => {
    const opt = document.createElement("option");
    opt.value = optLabel;
    opt.textContent = optLabel;
    sel.appendChild(opt);
  });
  const normalizedValue = normalizeEventTypeValue(val);
  if (normalizedValue && !EVENT_TYPE_OPTIONS.some((opt) => opt === normalizedValue)) {
    const customOpt = document.createElement("option");
    customOpt.value = normalizedValue;
    customOpt.textContent = normalizedValue;
    sel.insertBefore(customOpt, sel.firstChild);
  }
  sel.value = normalizedValue || EVENT_TYPE_OPTIONS[0];
  return sel;
}


export function buildReadonlyTableCell(value) {
  const td = document.createElement("td");
  const inp = document.createElement("input");
  inp.type = "text";
  inp.className = "field-table__input";
  inp.readOnly = true;
  inp.tabIndex = -1;
  inp.value = value == null ? "" : String(value);
  td.appendChild(inp);
  return td;
}


export function renderHeaderFormTypeRow(headerFormType, headerId) {
  if (headerId == null) return null;
  const tr = document.createElement("tr");
  tr.className = "field-table__row";
  tr.dataset.headerId = String(headerId);
  tr.dataset.headerFormTypeRow = "1";

  const tdLabel = document.createElement("td");
  tdLabel.className = "field-table__label";
  tdLabel.title = "Header.FormType";
  tdLabel.textContent = "Event Type";
  tr.appendChild(tdLabel);
  tr.appendChild(buildReadonlyTableCell(""));

  const tdOrig = document.createElement("td");
  const sel = document.createElement("select");
  sel.className = "field-table__input header-formtype-select";
  sel.dataset.headerId = String(headerId);
  HEADER_EVENT_TYPE_OPTIONS.forEach((optLabel) => {
    const opt = document.createElement("option");
    opt.value = optLabel;
    opt.textContent = optLabel;
    sel.appendChild(opt);
  });
  const normalizedValue = normalizeHeaderEventTypeValue(headerFormType);
  if (normalizedValue && !HEADER_EVENT_TYPE_OPTIONS.some((opt) => opt === normalizedValue)) {
    const customOpt = document.createElement("option");
    customOpt.value = normalizedValue;
    customOpt.textContent = normalizedValue;
    sel.insertBefore(customOpt, sel.firstChild);
  }
  sel.value = normalizedValue || HEADER_EVENT_TYPE_OPTIONS[0];
  sel.dataset.prevValue = sel.value;
  tdOrig.appendChild(sel);
  tr.appendChild(tdOrig);
  tr.appendChild(buildReadonlyTableCell(""));
  return tr;
}


export function rowAllEmptyFromInputs(tr) {
  let hasAny = false;
  tr.querySelectorAll("[data-col]").forEach((inp) => {
    if ((inp.value || "").trim() !== "") hasAny = true;
  });
  return !hasAny;
}

/**
 * Une ligne de tableau : libellé | hwr | orig | conf
 */

export function renderFieldRow(base, meta, recordId, headerIdOverride, options) {
  const headerId =
    recordId == null
      ? headerIdOverride != null && headerIdOverride !== undefined
        ? headerIdOverride
        : state.work && state.work.header_id != null
          ? state.work.header_id
          : null
      : null;
  const rekeyThreshold = resolveFieldRekeyThreshold(base, meta);
  const rowMeta = { ...meta, threshold: rekeyThreshold };
  const low = fieldLowConf(rowMeta, meta.conf, base);
  const meetsThreshold = fieldConfMeetsRekeyThreshold(base, meta.conf, rowMeta);
  // Blue highlight only for explicit Auto-correct / AI Page Fill markers — not DB *_edt
  // or low-conf orig≠hwr (those appear on virgin HWR pages and must not look "corrected").
  const autocorrected = hasPersistedAutocorrectMarker(base, recordId, headerId);
  const tr = document.createElement("tr");
  let rowClass = "field-table__row";
  if (low) rowClass += " field-table__row--lowconf";
  if (meetsThreshold) rowClass += " field-table__row--override-ok";
  if (autocorrected) rowClass += " field-table__row--dict-corrected";
  tr.className = rowClass;
  tr.dataset.base = base;
  tr.dataset.hasThreshold = rekeyThreshold == null ? "0" : "1";
  if (rekeyThreshold != null) tr.dataset.rekeyThreshold = String(rekeyThreshold);
  if (meta.conf != null && String(meta.conf).trim() !== "") {
    tr.dataset.confRaw = String(meta.conf).trim();
  }
  tr.dataset.allEmpty = fieldAllVariantsEmpty(meta) ? "1" : "0";
  tr.dataset.rect = meta.rect || "";
  if (recordId != null) tr.dataset.recordId = String(recordId);
  if (recordId == null && headerId != null) tr.dataset.headerId = String(headerId);

  const tdLabel = document.createElement("td");
  tdLabel.className = "field-table__label";
  const fieldContext = options && options.fieldContext;
  const resolvedMeta = fieldContext
    ? (() => {
        const hit = fieldContext.fields.find((f) => String(f.fieldName || "") === String(base || ""));
        return {
          fieldLabel: normalizeUiText(
            (hit && hit.fieldLabel) || resolveFieldLabel(base, meta.label)
          ),
          fieldName: (hit && hit.fieldName) || String(base || ""),
        };
      })()
    : resolveFieldMeta(base, meta.label);
  const resolvedLabel = normalizeUiText(resolvedMeta.fieldLabel);
  tdLabel.title = resolvedMeta.fieldName || String(base || "");
  tdLabel.innerHTML = escapeHtml(resolvedLabel);

  function cellInput(col, val, readOnly) {
    const td = document.createElement("td");
    const origPersistable = col.endsWith("_orig") ? isOrigBasePersistable(base, recordId) : true;
    const effectiveReadOnly = readOnly || (col.endsWith("_orig") && !origPersistable);
    const isEventTypeOrig =
      !effectiveReadOnly && col.endsWith("_orig") && isEventTypeFieldBase(base);
    if (isEventTypeOrig) {
      td.appendChild(buildEventTypeOrigSelect(col, val));
      return td;
    }

    const inp = document.createElement("input");
    inp.type = "text";
    inp.className = "field-table__input";
    inp.dataset.col = col;
    inp.readOnly = effectiveReadOnly;
    if (effectiveReadOnly) {
      // Champs visibles mais exclus de la navigation Tab.
      inp.tabIndex = -1;
    }
    inp.value = val == null ? "" : String(val);
    if (effectiveReadOnly && !readOnly) {
      inp.title = "Non modifiable pour ce DocType";
    }
    td.appendChild(inp);
    return td;
  }

  function cellReadonlyWithBadge(col, val, badgeText) {
    const td = document.createElement("td");
    td.className = "field-table__readonly-badge-cell";
    const wrap = document.createElement("div");
    wrap.className = "field-table__readonly-badge-wrap";
    const inp = document.createElement("input");
    inp.type = "text";
    inp.className = "field-table__input";
    inp.dataset.col = col;
    inp.readOnly = true;
    inp.tabIndex = -1;
    inp.value = val == null ? "" : String(val);
    wrap.appendChild(inp);
    if (badgeText) {
      const badge = document.createElement("span");
      badge.className = "field-table__conf-badge";
      badge.textContent = badgeText;
      wrap.appendChild(badge);
    }
    td.appendChild(wrap);
    return td;
  }

  function cellOrigMerged(col, val, hwrText, badgeText) {
    const td = document.createElement("td");
    td.className = "field-table__orig-merged-cell";

    const wrap = document.createElement("div");
    wrap.className = "field-table__orig-merged-wrap";

    const inputWrap = document.createElement("div");
    inputWrap.className = "field-table__orig-merged-input-wrap";

    const origPersistable = isOrigBasePersistable(base, recordId);
    const isEventTypeOrig =
      origPersistable && col.endsWith("_orig") && isEventTypeFieldBase(base);
    if (isEventTypeOrig) {
      inputWrap.appendChild(buildEventTypeOrigSelect(col, val));
    } else {
      const inp = document.createElement("input");
      inp.type = "text";
      inp.className = "field-table__input";
      inp.dataset.col = col;
      inp.readOnly = !origPersistable;
      if (!origPersistable) inp.tabIndex = -1;
      inp.value = val == null ? "" : String(val);
      if (!origPersistable) inp.title = "Non modifiable pour ce DocType";
      inputWrap.appendChild(inp);
    }

    const hasBadge = String(badgeText == null ? "" : badgeText).trim() !== "";
    wrap.appendChild(inputWrap);
    if (state.showMergedHwrText) {
      const hwrValue = String(hwrText == null ? "" : hwrText).trim();
      if (hwrValue) {
        const hwrLine = document.createElement("div");
        hwrLine.className = "field-table__orig-merged-hwr-line";
        const hwrLabel = document.createElement("span");
        hwrLabel.textContent = "hwr: " + hwrValue;
        hwrLine.appendChild(hwrLabel);
        if (hasBadge) {
          const badge = document.createElement("span");
          badge.className = "field-table__conf-badge field-table__conf-badge--hwr-line";
          badge.textContent = String(badgeText);
          hwrLine.appendChild(badge);
        }
        wrap.appendChild(hwrLine);
      }
    }

    td.appendChild(wrap);
    return td;
  }

  const confPercentDisplay = formatConfPercentDisplay(meta.conf);
  const hwrDisplay = meta.hwr == null ? "" : String(meta.hwr);

  tr.appendChild(tdLabel);
  tr.appendChild(
    cellReadonlyWithBadge(base + "_hwr", hwrDisplay, confPercentDisplay)
  );
  if (state.mergeHwrOrigColumn) {
    tr.appendChild(
      cellOrigMerged(
        base + "_orig",
        meta.orig != null ? meta.orig : "",
        hwrDisplay,
        confPercentDisplay
      )
    );
  } else {
    tr.appendChild(cellInput(base + "_orig", meta.orig != null ? meta.orig : "", false));
  }
  tr.appendChild(cellInput(base + "_conf", confPercentDisplay, true));
  return tr;
}


export function buildEmptyFieldMeta(base) {
  const meta = {
    label: resolveFieldLabel(base, base),
    threshold: null,
    orig: "",
    hwr: "",
    conf: "",
    rect: "",
  };
  const th = resolveFieldRekeyThreshold(base, meta);
  if (th != null) meta.threshold = th;
  return meta;
}


export function buildFieldTable(rows) {
  const table = document.createElement("table");
  table.className = "field-table field-table--hide-conf-column";
  table.classList.toggle("field-table--merge-hwr-orig", !!state.mergeHwrOrigColumn);
  const thead = document.createElement("thead");
  const hr = document.createElement("tr");
  ["Champ", "hwr", "orig.", "conf"].forEach((text) => {
    const th = document.createElement("th");
    th.textContent = text;
    hr.appendChild(th);
  });
  thead.appendChild(hr);
  const tbody = document.createElement("tbody");
  rows.forEach((tr) => tbody.appendChild(tr));
  table.appendChild(thead);
  table.appendChild(tbody);
  return table;
}


export function buildRecordInitialUpdates(recordSnapshot) {
  const updates = {};
  const fields = (recordSnapshot && recordSnapshot.fields) || {};
  Object.keys(fields).forEach((base) => {
    const meta = fields[base] || {};
    updates[base + "_orig"] = meta.orig == null ? "" : String(meta.orig);
    updates[base + "_hwr"] = meta.hwr == null ? "" : String(meta.hwr);
    updates[base + "_conf"] = meta.conf == null ? "" : String(meta.conf);
    if (meta.edt != null) {
      updates[base + "_edt"] = String(meta.edt);
    }
  });
  return updates;
}


export function syncRecordFieldsFromDom() {
  if (!state.work || !Array.isArray(state.work.records)) return;
  const cards = document.querySelectorAll("#recordsBlock .record-card");
  cards.forEach((card) => {
    const ridRaw = card.dataset.recordId;
    if (ridRaw == null || ridRaw === "") return;
    const rec = state.work.records.find((r) => r && String(r.record_id) === String(ridRaw));
    if (!rec) return;
    if (!rec.fields) rec.fields = {};
    card.querySelectorAll(".field-table__input[data-col]").forEach((inp) => {
      const col = String(inp.dataset.col || "");
      if (!col.endsWith("_orig")) return;
      const base = col.slice(0, -"_orig".length);
      if (!base) return;
      if (!rec.fields[base]) rec.fields[base] = buildEmptyFieldMeta(base);
      rec.fields[base].orig = inp.value;
    });
  });
}


export function buildEmptyRecordFieldsTemplate() {
  const fields = {};
  const recs = state.work && Array.isArray(state.work.records) ? state.work.records : [];
  const source = recs.find((r) => r && r.fields && Object.keys(r.fields).length) || null;
  if (!source) return fields;
  Object.keys(source.fields).forEach((base) => {
    fields[base] = buildEmptyFieldMeta(base);
  });
  return fields;
}


/** Update the record counter shown at the top of the form pane. */
export function updateFormRecordCount() {
  const bar = $("formRecordCountBar");
  const valueEl = $("formRecordCount");
  if (!bar || !valueEl) return;
  const docType = (state.work && state.work.form_type) || state.docType || "";
  if (isHeaderKeyOnlyDocType(docType) || isActiveWorkNonDataDocType(docType)) {
    bar.hidden = true;
    return;
  }
  const records =
    state.work && Array.isArray(state.work.records) ? state.work.records : [];
  valueEl.textContent = String(records.length);
  bar.hidden = false;
}

export function renderRecordsBlock() {
  const rb = $("recordsBlock");
  if (!rb) return;
  rb.innerHTML = "";
  if (
    isHeaderKeyOnlyDocType((state.work && state.work.form_type) || state.docType || "")
    || isActiveWorkNonDataDocType()
  ) {
    updateFormRecordCount();
    return;
  }
  const nonDataMode = isActiveWorkNonDataDocType();
  const records = state.work && Array.isArray(state.work.records) ? state.work.records : [];

  function createInsertButton(insertIndex) {
    const wrap = document.createElement("div");
    wrap.className = "record-insert-wrap";
    const btn = document.createElement("button");
    btn.type = "button";
    btn.className = "btn btn--small record-insert-btn";
    btn.textContent = "+ Ajouter un record";
    btn.dataset.action = "add-record";
    btn.dataset.insertIndex = String(insertIndex);
    wrap.appendChild(btn);
    return wrap;
  }

  if (!records.length) {
    const p = document.createElement("p");
    p.className = "muted";
      p.textContent = "Aucun enregistrement (Record) li\u00e9 \u00e0 cette image dans la base.";
    rb.appendChild(p);
    if (!nonDataMode) {
      rb.appendChild(createInsertButton(0));
    }
    normalizeUiTree(rb);
    updateFormRecordCount();
    return;
  }

  records.forEach((rec, idx) => {
    const card = document.createElement("div");
    card.className = "record-card";
    card.dataset.recordId = String(rec.record_id);
    card.dataset.recordIndex = String(idx);

    const head = document.createElement("div");
    head.className = "record-card__head";
    const h = document.createElement("h3");
    h.textContent = "RecordID " + (idx + 1);
    head.appendChild(h);

    const headers = state.work && Array.isArray(state.work.headers) ? state.work.headers : [];
    if (headers.length) {
      const selWrap = document.createElement("label");
      selWrap.className = "record-header-sel-wrap";
      const selLab = document.createElement("span");
      selLab.className = "record-header-sel-label";
      selLab.textContent = "Header";
      const sectionSel = document.createElement("select");
      sectionSel.className = "record-header-sel";
      sectionSel.setAttribute("aria-label", "Header section for this record");
      sectionSel.dataset.recordIndex = String(idx);
      headers.forEach((hdr, hi) => {
        const opt = document.createElement("option");
        opt.value = String(hdr.header_id);
        opt.textContent = "Section " + (hi + 1);
        if (Number(hdr.header_id) === Number(rec.header_id)) opt.selected = true;
        sectionSel.appendChild(opt);
      });
      selWrap.appendChild(selLab);
      selWrap.appendChild(sectionSel);
      head.appendChild(selWrap);
    }

    const moveWrap = document.createElement("div");
    moveWrap.className = "record-card__move";
    const upBtn = document.createElement("button");
    upBtn.type = "button";
    upBtn.className = "btn btn--small record-move-btn";
    upBtn.dataset.action = "move-record-up";
    upBtn.dataset.recordIndex = String(idx);
    upBtn.title = "Monter ce record";
    upBtn.textContent = "\u2191";
    if (findMoveTargetIndex(idx, "up") < 0) upBtn.disabled = true;
    moveWrap.appendChild(upBtn);

    const downBtn = document.createElement("button");
    downBtn.type = "button";
    downBtn.className = "btn btn--small record-move-btn";
    downBtn.dataset.action = "move-record-down";
    downBtn.dataset.recordIndex = String(idx);
    downBtn.title = "Descendre ce record";
    downBtn.textContent = "\u2193";
    if (findMoveTargetIndex(idx, "down") < 0) downBtn.disabled = true;
    moveWrap.appendChild(downBtn);

    if (idx === 0 && resolveFieldConfigRootKind(getCurrentProjectKind()) === "BDM") {
      const canTransferToPrev = state.imageIndex > 0;
      const transferBtn = document.createElement("button");
      transferBtn.type = "button";
      transferBtn.className = "btn btn--small record-transfer-btn";
      transferBtn.dataset.action = "transfer-record-to-prev-image";
      transferBtn.dataset.recordIndex = String(idx);
      transferBtn.title = "Transférer ce record vers l'image précédente";
      transferBtn.setAttribute("aria-label", "Transférer ce record vers l'image précédente");
      transferBtn.textContent = "\u25A3";
      transferBtn.disabled = !canTransferToPrev;
      moveWrap.appendChild(transferBtn);

      const mergeBtn = document.createElement("button");
      mergeBtn.type = "button";
      mergeBtn.className = "btn btn--small record-merge-btn";
      mergeBtn.dataset.action = "merge-record-to-prev-image";
      mergeBtn.dataset.recordIndex = String(idx);
      mergeBtn.title = "Fusionner avec le dernier record de l'image précédente";
      mergeBtn.setAttribute("aria-label", "Fusionner avec le dernier record de l'image précédente");
      mergeBtn.textContent = "\u29C9";
      mergeBtn.disabled = !canTransferToPrev;
      moveWrap.appendChild(mergeBtn);
    }

    head.appendChild(moveWrap);

    const removeBtn = document.createElement("button");
    removeBtn.type = "button";
    removeBtn.className = "btn btn--small record-remove-btn";
    removeBtn.dataset.action = "remove-record";
    removeBtn.dataset.recordIndex = String(idx);
    removeBtn.title = "Supprimer ce record";
    removeBtn.textContent = "Supprimer";
    head.appendChild(removeBtn);
    card.appendChild(head);

    const fields = document.createElement("div");
    fields.className = "fields fields--table";
    const fieldContext = getConfiguredFieldContextForRecord(rec);
    const recordBases = getVisibleFieldBases(rec.fields || {}, { fieldContext });
    const actRows = recordBases
      .filter((base) => !isHiddenField(base, rec.fields[base] || {}, { fieldContext }))
      .map((base) => renderFieldRow(
        base,
        rec.fields[base] || buildEmptyFieldMeta(base),
        rec.record_id,
        undefined,
        { fieldContext }
      ));
    fields.appendChild(buildFieldTable(actRows));
    card.appendChild(fields);
    rb.appendChild(card);

    if (!nonDataMode) {
      rb.appendChild(createInsertButton(idx + 1));
    }
  });
  normalizeUiTree(rb);
  updateFormRecordCount();
}


export function applyEmptyFilter() {
  const cb = $("filterEmptyOnly");
  if (!cb) return;
  const hide = cb.checked;
  document.querySelectorAll("#formContent .field-table__row").forEach((tr) => {
    const allEmpty = tr.dataset.allEmpty === "1";
    const hideBecauseEmpty = hide && allEmpty;
    tr.hidden = !!hideBecauseEmpty;
  });
}


export function measureIntrinsicScrollWidth(el) {
  const prev = el.style.whiteSpace;
  el.style.whiteSpace = "nowrap";
  const w = el.scrollWidth;
  el.style.whiteSpace = prev || "";
  return w;
}

/**
 * Colonne « Champ » : plus long libellé + 4 px, sans dépasser ~70 % du tableau.
 * Ne pas rappeler au filtre « champs vides » : le masquage de lignes change le layout et fausse la mesure.
 */

export function applyFieldTableColumnWidths() {
  const fc = $("formContent");
  if (!fc || fc.hidden) return;
  fc.querySelectorAll(".field-table").forEach((table) => {
    let max = 0;
    table.querySelectorAll("tbody td:first-child .field-table__label").forEach(
      (el) => {
        max = Math.max(max, measureIntrinsicScrollWidth(el));
      }
    );
    table.querySelectorAll("thead th:first-child").forEach((th) => {
      max = Math.max(max, measureIntrinsicScrollWidth(th));
    });
    const tw = table.getBoundingClientRect().width;
    if (tw < 8) return;
    const marge = 4;
    const plafond = Math.floor(tw * 0.7);
    const wpx = Math.min(plafond, Math.max(48, max + marge));
    table.style.setProperty("--col-champ-px", wpx + "px");
  });
}

/**
 * Image aux dimensions natives (px) + zoom initial pour voir tout le document dans la zone.
 */
