import { fieldsByKindConfig } from '../../config/fields-config.js';
import { SUB_PROJECT_LANGUAGES } from '../../config/constants.js';
import { $ } from '../../core/dom.js';
import { api } from '../../core/api.js';
import { normalizeUiTree } from '../../core/text.js';
import { loadDocTypesForBatch, refreshCurrentWorkView } from '../../services/batch.js';
import { buildFieldSelectionKey, getOrderedFieldEntries, isAlwaysSelectedDocType, isFieldDefaultSelected } from '../../services/field-selections.js';
import { markFieldSelectionsDirty } from '../../services/preferences.js';
import { _getEffectiveFieldConfig, getCurrentProjectKind, normalizeSearchText, resolveFieldLabel } from '../../services/project-context.js';
import { settingsUiState, state, subProfilePanelState, subProjectEditorState, runtime } from '../../state/app-state.js';
import { _applySubProfileFieldSearchFilter, _buildDefaultFieldsFromConfig, _editorDocTypes, _editorDoctypeFields, _editorFields, _editorKeyingInstructions, _profilePanelUpdateEditChrome, _renderProfilePanel, _setCiImportStatus, _setEditorDocTypes, _setEditorDoctypeFields, _setEditorFields, _setEditorKeyingInstructions, cancelProfilePanelThresholdEdit, getAssignableSubProjects, moveFieldOrderBefore, normalizeFieldsKindTab, setActiveFieldsKindTab, setActiveSettingsTab } from '../events.js';
import { loadGatewayActivationStatus } from '../gateway-activation.js';
import { getCiAssetsForSave, getCiBatchManifestForSave, getCiProfileForSave, loadPersistedCiProfile, resetCiImportState, setCiImportPayload } from '../ci-import-state.js';
import { activateSubProjectProfileTab, initSubProjectCiProfileTabs } from '../sub-project-ci-profile.js';
import { showSubProjectDeleteConfirmPanel } from '../modals.js';
import { setStatus } from '../status-bar.js';
import { syncFontSizeSettingsUi } from '../theme.js';
import {
  initProjectKindsLockUi,
  isProjectKindsProtected,
  promptChefPasswordForAction,
  refreshProjectKindsLockStatus,
  syncProjectKindsLockUi,
  withChefPassword,
} from './project-kinds-lock.js';


/**
 * Populate a language select with the fixed sub-project language list.
 * Maps legacy English/Arabic labels and preserves other unknown values as options.
 */
export function fillSubProjectLanguageSelect(selectEl, selectedValue) {
  if (!(selectEl instanceof HTMLSelectElement)) return;
  const legacyAliases = { English: "Anglais", Arabic: "Arabe", French: "Français" };
  const raw = String(selectedValue || "").trim();
  const selected = legacyAliases[raw] || raw;
  const known = SUB_PROJECT_LANGUAGES.slice();
  selectEl.innerHTML = "";
  const empty = document.createElement("option");
  empty.value = "";
  empty.textContent = "Sélectionner\u2026";
  selectEl.appendChild(empty);
  if (selected && known.indexOf(selected) < 0) {
    const legacy = document.createElement("option");
    legacy.value = selected;
    legacy.textContent = selected;
    selectEl.appendChild(legacy);
  }
  known.forEach((lang) => {
    const opt = document.createElement("option");
    opt.value = lang;
    opt.textContent = lang;
    selectEl.appendChild(opt);
  });
  selectEl.value = selected;
}


async function refreshActiveBatchDocTypeConfiguration() {
  if (!state.batch) return;
  try {
    await loadDocTypesForBatch();
    await refreshCurrentWorkView();
  } catch (_) {
    // The sub-project save succeeded; a later batch/image load will retry this refresh.
  }
}

export function closeSubProjectAssignPanel() {
  runtime.subProjectAssignSelection = null;
  const panel = $("subProjectAssignPanel");
  const backdrop = $("subProjectAssignBackdrop");
  if (panel) panel.hidden = true;
  if (backdrop) backdrop.hidden = true;
}


/**
 * Prompt for Chef password (when protected), then open the lot assign panel.
 * @param {object} project
 * @returns {Promise<void>}
 */
export async function startEditSubProjectAssignment(project) {
  const chefPassword = await promptChefPasswordForAction(
    "Saisissez le mot de passe Chef de projet pour modifier l'affectation du lot."
  );
  if (chefPassword === null) return;
  openSubProjectAssignPanel(project, chefPassword);
}

/**
 * Open the lot sub-project assignment panel.
 * @param {object} project
 * @param {string | null} [pendingChefPassword] password collected before open
 */
export function openSubProjectAssignPanel(project, pendingChefPassword = null) {
  const panel = $("subProjectAssignPanel");
  const backdrop = $("subProjectAssignBackdrop");
  const titleEl = $("subProjectAssignTitle");
  const msgEl = $("subProjectAssignMessage");
  const selectEl = $("subProjectAssignSelect");
  if (!panel || !backdrop || !titleEl || !msgEl || !selectEl) return;

  const lotName = String((project && project.project_name) || "").trim();
  const rootKind = String((project && project.kind) || "").trim();
  const currentSubProject = String((project && project.sub_project) || "").trim();
  const options = getAssignableSubProjects(rootKind);
  if (!lotName) {
    setStatus("Lot introuvable.", true);
    return;
  }
  if (!options.length) {
    setStatus("Aucun sous-projet disponible pour ce type.", true);
    return;
  }

  runtime.subProjectAssignSelection = {
    lotName,
    pendingChefPassword:
      pendingChefPassword == null ? null : String(pendingChefPassword),
  };
  titleEl.textContent = "Changer le sous-projet";
  msgEl.textContent = `Lot "${lotName}"`;
  selectEl.innerHTML = "";
  options.forEach((name) => {
    const opt = document.createElement("option");
    opt.value = name;
    opt.textContent = name;
    selectEl.appendChild(opt);
  });
  if (currentSubProject && options.includes(currentSubProject)) {
    selectEl.value = currentSubProject;
  } else {
    selectEl.selectedIndex = 0;
  }

  panel.hidden = false;
  backdrop.hidden = false;
  selectEl.focus();
}


export function syncShortcutsStartupOptionUi() {
  const opt = $("optShowShortcutsOnStartup");
  if (opt) opt.checked = !!state.showShortcutsOnStartup;
}


export function toggleSettingsPanel(open) {
  const panel = $("settingsPanel");
  if (!panel) return;
  const mainLayout = $("mainLayout");
  state.settingsOpen = !!open;
  panel.hidden = !state.settingsOpen;
  if (mainLayout) mainLayout.hidden = state.settingsOpen;
  document.body.classList.toggle("settings-page-open", state.settingsOpen);
  const btn = $("menuSettingsOpen");
  if (btn) btn.setAttribute("aria-expanded", state.settingsOpen ? "true" : "false");
}


export function closeSettingsPanel(reason = "manual") {
  toggleSettingsPanel(false);
}


export async function openSettingsPanel() {
  if (state.settingsOpen || settingsUiState.opening) return;
  settingsUiState.opening = true;
  toggleSettingsPanel(true);
  settingsUiState.lastOpenedAtMs = Date.now();
  syncFontSizeSettingsUi();
  try {
    // Preload shared settings data; do not force the Champs tab open.
    const preloadTasks = [loadSubProjects(), refreshProjectKindsLockStatus().catch(() => null)];
    if (!settingsUiState.projectKindsLoadedOnce) {
      if (!settingsUiState.projectKindsInFlight) {
        settingsUiState.projectKindsInFlight = loadProjectKinds().finally(() => {
          settingsUiState.projectKindsInFlight = null;
        });
      }
      preloadTasks.push(settingsUiState.projectKindsInFlight);
    }
    await Promise.all(preloadTasks);
    // Always open on the first tab (Types de projet).
    setActiveSettingsTab("projectKinds");
    syncProjectKindsLockUi();
  } finally {
    settingsUiState.opening = false;
  }
}


export function initSettingsTabs() {
  const tabs = Array.from(document.querySelectorAll(".settings-tab"));
  tabs.forEach((tab) => {
    tab.addEventListener("click", () => {
      setActiveSettingsTab(tab.dataset.settingsTab);
    });
  });
}


export function renderFieldsKindTabs() {
  const container = $("fieldsKindTabsContainer");
  if (!container) return;
  container.innerHTML = "";
  const tabs =
    state.subProjects && state.subProjects.length
      ? state.subProjects.map((sp) => ({ id: sp.name, label: sp.name }))
      : [
          { id: "Census", label: "Census" },
          { id: "BDM", label: "BDM" },
        ];

  tabs.forEach(({ id, label }) => {
    const btn = document.createElement("button");
    btn.type = "button";
    btn.className = "fields-kind-tab";
    btn.dataset.kindTab = id;
    btn.setAttribute("role", "tab");
    btn.setAttribute("aria-selected", "false");
    btn.textContent = label;
    btn.addEventListener("click", () => setActiveFieldsKindTab(id));
    container.appendChild(btn);
  });
  // Re-activate the correct tab after rebuilding
  setActiveFieldsKindTab(state.selectedFieldsKind);
}


export function syncFieldsKindTabToCurrentProject() {
  setActiveFieldsKindTab(getCurrentProjectKind());
}


export function initFieldsKindTabs() {
  // Tabs are generated dynamically; renderFieldsKindTabs() wires the listeners.
}


export function renderFieldsByKind(kind) {
  const host = $("fieldsByKindContainer");
  if (!host) return;
  const config = _getEffectiveFieldConfig(kind);
  const docTypes = Object.keys(config);
  const normalizedQuery = normalizeSearchText(state.fieldSearchQuery);
  host.innerHTML = "";

  if (!docTypes.length) {
    const empty = document.createElement("div");
    empty.className = "fields-empty";
    empty.textContent = "Aucun champ configuré pour ce type de projet.";
    host.appendChild(empty);
    return;
  }

  const grid = document.createElement("div");
  grid.className = "fields-doctype-grid";
  docTypes.forEach((docType) => {
    const card = document.createElement("section");
    card.className = "fields-doctype-card" + (docType === "__HEADER__" ? " fields-doctype-card--header" : "");

    const title = document.createElement("h3");
    title.className = "fields-doctype-title";
    if (docType === "__HEADER__") {
      title.textContent = "En-tête";
    } else {
      title.textContent = docType === "__GENERAL__" ? "Général (pour tous les EventType)" : docType;
    }
    const mixedEventTypePrefix = "Mixed \u2192 ";
    if (typeof docType === "string" && docType.startsWith(mixedEventTypePrefix)) {
      title.classList.add("fields-doctype-title--mixed-eventtype");
    }
    card.appendChild(title);

    const list = document.createElement("div");
    list.className = "fields-check-list";

    const fields = config[docType] || [];
    const orderedEntries = getOrderedFieldEntries(kind, docType, fields);
    const filteredEntries = !normalizedQuery
      ? orderedEntries
      : orderedEntries.filter((entry) => {
          const field = entry.field || {};
          const label = normalizeSearchText(field.fieldLabel || "");
          const name = normalizeSearchText(field.fieldName || "");
          return label.includes(normalizedQuery) || name.includes(normalizedQuery);
        });

    if (!filteredEntries.length) {
      const noMatch = document.createElement("div");
      noMatch.className = "fields-empty fields-empty--inline";
      noMatch.textContent = normalizedQuery ? "Aucun champ correspondant." : "Aucun champ configuré.";
      list.appendChild(noMatch);
    }

    filteredEntries.forEach((entry, index) => {
      const field = entry.field;
      const row = document.createElement("div");
      row.className = "fields-check fields-check--draggable";
      row.dataset.orderId = entry.id;
      row.addEventListener("dragenter", (e) => {
        e.preventDefault();
        row.classList.add("is-drop-target");
      });
      row.addEventListener("dragover", (e) => {
        e.preventDefault();
        e.dataTransfer.dropEffect = "move";
        row.classList.add("is-drop-target");
      });
      row.addEventListener("dragleave", () => {
        row.classList.remove("is-drop-target");
      });
      row.addEventListener("drop", async (e) => {
        e.preventDefault();
        row.classList.remove("is-drop-target");
        const fromId = e.dataTransfer.getData("text/plain");
        const didMove = moveFieldOrderBefore(kind, docType, fields, fromId, entry.id);
        if (!didMove) return;
        markFieldSelectionsDirty();
        renderFieldsByKind(kind);
        await refreshCurrentWorkView();
      });

      const dragHandle = document.createElement("span");
      dragHandle.className = "fields-check__drag";
      dragHandle.draggable = true;
      dragHandle.title = "Drag to reorder";
      dragHandle.setAttribute("aria-label", "Drag to reorder");
      dragHandle.addEventListener("dragstart", (e) => {
        e.dataTransfer.effectAllowed = "move";
        e.dataTransfer.setData("text/plain", entry.id);
        row.classList.add("is-dragging");
      });
      dragHandle.addEventListener("dragend", () => {
        row.classList.remove("is-dragging");
        row.classList.remove("is-drop-target");
      });

      const checkbox = document.createElement("input");
      checkbox.type = "checkbox";
      const alwaysSelected = isAlwaysSelectedDocType(docType);
      const selectionKey = buildFieldSelectionKey(kind, docType, field.fieldName || "");
      const displayLabel = resolveFieldLabel(
        field.fieldName || "",
        field.fieldLabel || field.fieldName || ""
      );
      if (!Object.prototype.hasOwnProperty.call(state.fieldSelections, selectionKey)) {
        state.fieldSelections[selectionKey] = isFieldDefaultSelected(docType, field);
      }
      checkbox.checked = !!state.fieldSelections[selectionKey];
      checkbox.dataset.kind = kind;
      checkbox.dataset.docType = docType;
      checkbox.dataset.fieldName = field.fieldName || "";
      checkbox.dataset.fieldLabel = displayLabel;
      checkbox.id = `field_${kind}_${docType}_${index}`;
      checkbox.addEventListener("change", async () => {
        state.fieldSelections[selectionKey] = checkbox.checked;
        markFieldSelectionsDirty();
        await refreshCurrentWorkView();
      });

      const labelText = document.createElement("label");
      labelText.className = "fields-check__label";
      labelText.textContent = displayLabel;
      labelText.dataset.fieldName = field.fieldName || "";
      labelText.setAttribute("for", checkbox.id);

      row.appendChild(dragHandle);
      row.appendChild(checkbox);
      row.appendChild(labelText);
      list.appendChild(row);
    });

    card.appendChild(list);
    grid.appendChild(card);
  });

  host.appendChild(grid);
  normalizeUiTree(host);
}


export function renderProjectKinds(projects, summary) {
  const tbody = $("projectKindsTableBody");
  if (!tbody) return;
  const detectedLotsCount = $("detectedLotsCount");
  if (detectedLotsCount) {
    const uniqueLots = new Set(
      (Array.isArray(projects) ? projects : [])
        .map((project) => String(project && project.project_name ? project.project_name : "").trim())
        .filter(Boolean)
    );
    detectedLotsCount.textContent = String(uniqueLots.size);
  }

  tbody.innerHTML = "";
  projects.forEach((project) => {
    const tr = document.createElement("tr");

    const nameTd = document.createElement("td");
    nameTd.textContent = project.project_name || "";
    tr.appendChild(nameTd);

    const kindTd = document.createElement("td");
    const badge = document.createElement("span");
    const isCensus = String(project.kind || "").toLowerCase() === "census";
    badge.className = "kind-badge " + (isCensus ? "kind-badge--census" : "kind-badge--bdm");
    badge.textContent = isCensus ? "Census" : "BDM";
    kindTd.appendChild(badge);
    tr.appendChild(kindTd);

    const subTd = document.createElement("td");
    subTd.className = "doc-types-list";
    const subContent = document.createElement("div");
    subContent.className = "kinds-subproject-cell";
    const sub =
      project.sub_project != null && String(project.sub_project).trim()
        ? String(project.sub_project).trim()
        : "";
    const subLabel = document.createElement("span");
    subLabel.className = "kinds-subproject-name";
    subLabel.textContent = sub || "-";
    subContent.appendChild(subLabel);
    if (sub) {
      const editBtn = document.createElement("button");
      editBtn.type = "button";
      editBtn.className = "btn btn--small btn--icon kinds-subproject-edit";
      editBtn.innerHTML =
        '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M3 17.25V21h3.75L17.8 9.94l-3.75-3.75L3 17.25zm17.71-10.04a1.003 1.003 0 0 0 0-1.42l-2.5-2.5a1.003 1.003 0 0 0-1.42 0l-1.96 1.96 3.75 3.75 2.13-2.79z"/></svg>';
      editBtn.title = isProjectKindsProtected()
        ? `Modifier le sous-type « ${sub} » (mot de passe requis)`
        : `Modifier le sous-type « ${sub} »`;
      editBtn.setAttribute("aria-label", `Modifier le sous-type ${sub}`);
      editBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        void startEditSubProjectAssignment(project);
      });
      subContent.appendChild(editBtn);
    }
    subTd.appendChild(subContent);
    tr.appendChild(subTd);

    const docTypesTd = document.createElement("td");
    docTypesTd.className = "doc-types-list";
    const docTypes = Array.isArray(project.doc_types) ? project.doc_types : [];
    docTypesTd.textContent = docTypes.length ? docTypes.join(", ") : "-";
    tr.appendChild(docTypesTd);

    tbody.appendChild(tr);
  });
  normalizeUiTree(tbody);
}


export async function loadProjectKinds() {
  try {
    const r = await api("/api/project-kinds");
    state.projectKinds = Array.isArray(r.projects) ? r.projects : [];
    renderProjectKinds(state.projectKinds, r.summary || {});
    settingsUiState.projectKindsLoadedOnce = true;
  } catch (e) {
    settingsUiState.projectKindsLoadedOnce = false;
    setStatus(e.message, true);
  }
}

// ----- DocType tags (CI import preview) -----

/**
 * Internal store for the current editor's DocTypes.
 * Each entry: { name: string, cat: "data" | "non_data", headerKeyOnly?: boolean }
 */

export async function handleCiFileImport(file) {
  if (!file) return;
  let chefPassword = subProjectEditorState.pendingChefPassword;
  if (chefPassword == null) {
    chefPassword = await promptChefPasswordForAction(
      "Saisissez le mot de passe Chef de projet pour importer le fichier CI."
    );
    if (chefPassword === null) return;
  }
  _setCiImportStatus("Lecture en cours\u2026", false);
  const fd = new FormData();
  fd.append("file", file);
  if (chefPassword) fd.append("chef_password", chefPassword);
  try {
    const r = await fetch("/api/parse-ci-xlsx", {
      method: "POST",
      credentials: "same-origin",
      body: fd,
    });
    const json = await r.json();
    if (!r.ok || !json.ok) {
      _setCiImportStatus(json.error || "Erreur de lecture", true);
      return;
    }
    const headerKeyOnly = new Set(
      (json.header_key_only || []).map((n) => String(n || "").trim().toLowerCase())
    );
    const data = (json.data || []).map((n) => ({
      name: n,
      cat: "data",
      headerKeyOnly: headerKeyOnly.has(String(n || "").trim().toLowerCase()),
    }));
    const nonData = (json.non_data || []).map((n) => ({ name: n, cat: "non_data" }));
    const all = [...data, ...nonData];
    _setEditorDocTypes(all);
    _setEditorFields(json.fields);
    _setEditorDoctypeFields(json.doctype_fields);
    _setEditorKeyingInstructions(json.keying_instructions);
    setCiImportPayload(json);
    const importedLanguage = String(json.ci_profile?.project?.language || "").trim();
    const languageSelect = $("subProjectEditorLanguage");
    if (importedLanguage && languageSelect && !String(languageSelect.value || "").trim()) {
      fillSubProjectLanguageSelect(languageSelect, importedLanguage);
    }
    const dtMsg = `${all.length} DocType${all.length !== 1 ? "s" : ""} import\u00e9${all.length !== 1 ? "s" : ""} (${data.length} Data, ${nonData.length} Non-Data)`;
    const dtSheetCount = Object.keys(_editorDoctypeFields).length;
    const keyingCount = _editorKeyingInstructions.length;
    const manifestSummary = json.ci_profile?.manifest_summary || {};
    const extraParts = [];
    if (_editorFields.length) {
      extraParts.push(`${_editorFields.length} champ${_editorFields.length !== 1 ? "s" : ""}`);
    }
    if (dtSheetCount) {
      extraParts.push(`${dtSheetCount} feuille${dtSheetCount !== 1 ? "s" : ""} DocType`);
    }
    if (keyingCount) {
      extraParts.push(`${keyingCount} instruction${keyingCount !== 1 ? "s" : ""}`);
    }
    if (Number(manifestSummary.key_count || 0) > 0) {
      extraParts.push(`${Number(manifestSummary.key_count)} lot${Number(manifestSummary.key_count) !== 1 ? "s" : ""}`);
    }
    const assetCount = Number(json.ci_profile?.asset_summary?.asset_count || 0);
    if (assetCount > 0) {
      extraParts.push(`${assetCount} exemple${assetCount !== 1 ? "s" : ""}`);
    }
    const fldMsg = extraParts.length ? ` \u2014 ${extraParts.join(", ")}` : "";
    _setCiImportStatus(dtMsg + fldMsg, false);
  } catch (e) {
    _setCiImportStatus(String(e && e.message ? e.message : e), true);
  }
}

// ----- Sub-project editor -----


export function closeSubProjectEditor() {
  subProjectEditorState.editingId = null;
  subProjectEditorState.pendingChefPassword = null;
  const wrap = $("subProjectEditorWrap");
  if (wrap) wrap.hidden = true;
  const backdrop = $("subProjectEditorBackdrop");
  if (backdrop) backdrop.hidden = true;
  const nameEl = $("subProjectEditorName");
  if (nameEl) nameEl.value = "";
  fillSubProjectLanguageSelect($("subProjectEditorLanguage"), "");
  document.querySelectorAll('input[name="subProjectRootKind"]').forEach((r) => {
    if (r instanceof HTMLInputElement) r.checked = r.value === "BDM";
  });
  const integralEl = $("subProjectEditorIntegralKeying");
  if (integralEl instanceof HTMLInputElement) integralEl.checked = false;
  _setEditorDocTypes([]);
  _setEditorFields([]);
  _setEditorDoctypeFields({});
  _setEditorKeyingInstructions([]);
  resetCiImportState();
  _setCiImportStatus("", false);
  const fileInput = $("subProjectCiFile");
  if (fileInput instanceof HTMLInputElement) fileInput.value = "";
}


export function openSubProjectEditor(existing) {
  const wrap = $("subProjectEditorWrap");
  const title = $("subProjectEditorTitle");
  const backdrop = $("subProjectEditorBackdrop");
  if (!wrap) return;
  if (existing && existing.id != null) {
    subProjectEditorState.editingId = Number(existing.id);
    if (title) title.textContent = "Modifier le sous-type";
    const nameEl = $("subProjectEditorName");
    if (nameEl) nameEl.value = String(existing.name || "");
    fillSubProjectLanguageSelect($("subProjectEditorLanguage"), existing.language);
    const rk = normalizeFieldsKindTab(existing.root_kind);
    document.querySelectorAll('input[name="subProjectRootKind"]').forEach((r) => {
      if (r instanceof HTMLInputElement) r.checked = r.value === rk;
    });
    const integralEl = $("subProjectEditorIntegralKeying");
    if (integralEl instanceof HTMLInputElement) {
      integralEl.checked = Boolean(existing.integral_keying);
    }
    const dts = Array.isArray(existing.doc_types) ? existing.doc_types : [];
    const headerKeyOnly = new Set(
      (Array.isArray(existing.header_key_only) ? existing.header_key_only : [])
        .map((n) => String(n || "").trim().toLowerCase())
    );
    _setEditorFields(existing.fields);
    _setEditorDoctypeFields(existing.doctype_fields);
    _setEditorKeyingInstructions(existing.keying_instructions);
    loadPersistedCiProfile(existing.ci_profile);
    const g = _editorDoctypeFields.__doc_type_groups__;
    let docTypeEntries;
    if (g && typeof g === "object") {
      const norm = (s) => String(s || "").trim().toLowerCase();
      const dataS = new Set((g.data || []).map(norm));
      const nonS = new Set((g.non_data || []).map(norm));
      docTypeEntries = dts.map((raw) => {
        const name = String(raw || "").trim();
        const nl = norm(name);
        let cat = "non_data";
        if (dataS.has(nl)) cat = "data";
        else if (nonS.has(nl)) cat = "non_data";
        return { name, cat, headerKeyOnly: cat === "data" && headerKeyOnly.has(nl) };
      });
    } else {
      docTypeEntries = dts.map((n) => ({ name: n, cat: "non_data", headerKeyOnly: false }));
    }
    _setEditorDocTypes(docTypeEntries);
    const dtSheetCount = Object.keys(_editorDoctypeFields).filter((k) => !k.startsWith("__")).length;
    const keyingCount = _editorKeyingInstructions.length;
    const extraParts = [];
    if (_editorFields.length) {
      extraParts.push(`${_editorFields.length} champ${_editorFields.length !== 1 ? "s" : ""}`);
    }
    if (dtSheetCount) {
      extraParts.push(`${dtSheetCount} feuille${dtSheetCount !== 1 ? "s" : ""} DocType`);
    }
    if (keyingCount) {
      extraParts.push(`${keyingCount} instruction${keyingCount !== 1 ? "s" : ""}`);
    }
    const fldSuffix = extraParts.length ? ` \u2014 ${extraParts.join(", ")}` : "";
    _setCiImportStatus(dts.length ? `${dts.length} DocType${dts.length !== 1 ? "s" : ""} charg\u00e9${dts.length !== 1 ? "s" : ""}${fldSuffix}` : "", false);
  } else {
    subProjectEditorState.editingId = null;
    if (title) title.textContent = "Nouveau sous-type";
    const nameEl = $("subProjectEditorName");
    if (nameEl) nameEl.value = "";
    fillSubProjectLanguageSelect($("subProjectEditorLanguage"), "");
    document.querySelectorAll('input[name="subProjectRootKind"]').forEach((r) => {
      if (r instanceof HTMLInputElement) r.checked = r.value === "BDM";
    });
    const integralNew = $("subProjectEditorIntegralKeying");
    if (integralNew instanceof HTMLInputElement) integralNew.checked = false;
    _setEditorDocTypes([]);
    _setEditorFields([]);
    _setEditorDoctypeFields({});
    _setEditorKeyingInstructions([]);
    resetCiImportState();
    _setCiImportStatus("", false);
  }
  const fileInput = $("subProjectCiFile");
  if (fileInput instanceof HTMLInputElement) fileInput.value = "";
  if (backdrop) backdrop.hidden = false;
  wrap.hidden = false;
  $("subProjectEditorName")?.focus?.();
}


export async function commitSubProject() {
  const nameEl = $("subProjectEditorName");
  const name = nameEl ? nameEl.value.trim() : "";
  const languageEl = $("subProjectEditorLanguage");
  const language = languageEl ? languageEl.value.trim() : "";
  const integralEl = $("subProjectEditorIntegralKeying");
  const integralKeying =
    integralEl instanceof HTMLInputElement ? Boolean(integralEl.checked) : false;
  const docTypes = _editorDocTypes.map((d) => d.name);
  const rootRadio = document.querySelector('input[name="subProjectRootKind"]:checked');
  const rootKind =
    rootRadio && rootRadio instanceof HTMLInputElement ? rootRadio.value : "BDM";
  const doctype_fields = Object.assign({}, _editorDoctypeFields);
  const gPayload = { data: [], non_data: [] };
  _editorDocTypes.forEach((d) => {
    const dn = String(d.name || "").trim();
    if (!dn) return;
    if (d.cat === "data") gPayload.data.push(dn);
    else gPayload.non_data.push(dn);
  });
  doctype_fields.__doc_type_groups__ = gPayload;
  const body = {
    name,
    root_kind: rootKind,
    language,
    integral_keying: integralKeying,
    doc_types: docTypes,
    fields: _editorFields,
    doctype_fields,
    header_key_only: _editorDocTypes
      .filter((d) => d.cat === "data" && d.headerKeyOnly)
      .map((d) => d.name),
    keying_instructions: _editorKeyingInstructions,
    ci_profile: getCiProfileForSave(),
  };
  const ciBatchManifest = getCiBatchManifestForSave();
  if (ciBatchManifest !== undefined) {
    body.ci_batch_manifest = ciBatchManifest;
  }
  const ciAssets = getCiAssetsForSave();
  if (ciAssets !== undefined) {
    body.ci_assets = ciAssets;
  }
  let chefPassword = subProjectEditorState.pendingChefPassword;
  if (chefPassword == null) {
    chefPassword = await promptChefPasswordForAction(
      "Saisissez le mot de passe Chef de projet pour enregistrer ce sous-type."
    );
    if (chefPassword === null) return;
  }
  const payload = withChefPassword(body, chefPassword);
  try {
    if (subProjectEditorState.editingId != null) {
      await api(`/api/sub-projects/${subProjectEditorState.editingId}`, {
        method: "PUT",
        body: JSON.stringify(payload),
      });
    } else {
      await api("/api/sub-projects", {
        method: "POST",
        body: JSON.stringify(payload),
      });
    }
    closeSubProjectEditor();
    await loadSubProjects();
    await loadProjectKinds();
    await refreshActiveBatchDocTypeConfiguration();
    setStatus("Sous-type enregistré.");
  } catch (e) {
    setStatus(String(e && e.message ? e.message : e), true);
  }
}


export async function deleteSubProjectById(subId, subName) {
  const ok = await showSubProjectDeleteConfirmPanel(String(subName || "").trim());
  if (!ok) return false;
  const chefPassword = await promptChefPasswordForAction(
    "Saisissez le mot de passe Chef de projet pour supprimer ce sous-type."
  );
  if (chefPassword === null) return false;
  try {
    await api(`/api/sub-projects/${Number(subId)}`, {
      method: "DELETE",
      body: JSON.stringify(withChefPassword({}, chefPassword)),
    });
    closeProfilePanel();
    await loadSubProjects();
    await loadProjectKinds();
    setStatus("Sous-type supprimé.");
    return true;
  } catch (e) {
    setStatus(String(e && e.message ? e.message : e), true);
    return false;
  }
}


export async function loadSubProjects() {
  try {
    const r = await api("/api/sub-projects");
    state.subProjects = Array.isArray(r.sub_projects) ? r.sub_projects : [];
    renderSubProjects(state.subProjects);
    renderFieldsKindTabs();
  } catch (e) {
    setStatus(String(e && e.message ? e.message : e), true);
  }
}


export function renderSubProjects(rows) {
  const tbody = $("subProjectsTableBody");
  if (!tbody) return;
  tbody.innerHTML = "";
  if (!rows || !rows.length) {
    const tr = document.createElement("tr");
    const td = document.createElement("td");
    td.colSpan = 4;
    td.className = "muted";
    td.textContent = "Aucun sous-type défini. Utilisez \u00ab\u00a0Ajouter\u00a0\u00bb pour en créer un.";
    tr.appendChild(td);
    tbody.appendChild(tr);
    normalizeUiTree(tbody);
    return;
  }
  rows.forEach((row) => {
    const tr = document.createElement("tr");
    tr.className = "sub-project-row";
    tr.setAttribute("tabindex", "0");
    tr.setAttribute("role", "button");
    tr.setAttribute("aria-label", `Voir le profil de ${row.name || ""}`);
    tr.addEventListener("click", () => openProfilePanel(row));
    tr.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") { e.preventDefault(); openProfilePanel(row); }
    });

    const nameTd = document.createElement("td");
    nameTd.className = "sub-project-name";
    nameTd.textContent = String(row.name || "");
    tr.appendChild(nameTd);

    const languageTd = document.createElement("td");
    languageTd.textContent = String(row.language || "").trim() || "\u2014";
    tr.appendChild(languageTd);

    const kindTd = document.createElement("td");
    const badge = document.createElement("span");
    const isCensus = String(row.root_kind || "").toLowerCase() === "census";
    badge.className = "kind-badge " + (isCensus ? "kind-badge--census" : "kind-badge--bdm");
    badge.textContent = isCensus ? "Census" : "BDM";
    kindTd.appendChild(badge);
    tr.appendChild(kindTd);

    const dtTd = document.createElement("td");
    dtTd.className = "doc-types-list";
    const dts = Array.isArray(row.doc_types) ? row.doc_types : [];
    dtTd.textContent = dts.length ? dts.join(", ") : "\u2014";
    tr.appendChild(dtTd);

    tbody.appendChild(tr);
  });
  normalizeUiTree(tbody);
}


/**
 * Prompt for Chef password (when protected), then open the create panel.
 * @returns {Promise<void>}
 */
async function startAddSubProject() {
  const chefPassword = await promptChefPasswordForAction(
    "Saisissez le mot de passe Chef de projet pour ajouter un sous-type."
  );
  if (chefPassword === null) return;
  subProjectEditorState.pendingChefPassword = chefPassword;
  openSubProjectEditor();
}

export function initSubProjectsSettings() {
  initProjectKindsLockUi();
  $("btnSubProjectAdd")?.addEventListener("click", () => void startAddSubProject());
  $("btnSubProjectCancel")?.addEventListener("click", () => closeSubProjectEditor());
  $("btnSubProjectEditorClose")?.addEventListener("click", () => closeSubProjectEditor());
  $("btnSubProjectSave")?.addEventListener("click", () => void commitSubProject());
  $("subProjectEditorBackdrop")?.addEventListener("click", () => closeSubProjectEditor());

  const btnImport = $("btnCiImport");
  const fileInput = $("subProjectCiFile");
  if (btnImport && fileInput) {
    btnImport.addEventListener("click", () => fileInput.click());
    fileInput.addEventListener("change", () => {
      const f = fileInput instanceof HTMLInputElement ? fileInput.files?.[0] : null;
      if (f) void handleCiFileImport(f);
    });
  }

  document.addEventListener("keydown", (e) => {
    if (e.key !== "Escape") return;
    const wrap = $("subProjectEditorWrap");
    if (!wrap || wrap.hidden) return;
    e.preventDefault();
    closeSubProjectEditor();
  });
}


// ----- Sub-project profile panel -----

/**
 * Build a flat, deduplicated field list from fieldsByKindConfig for the
 * given sub-project name (e.g. "Census" or "BDM").
 * Returns [] when the name has no matching hardcoded config.
 */

export async function openProfilePanel(subProject) {
  let sp = subProject;

  // Lazy-seed: if this sub-project has no fields yet AND its name matches a
  // hardcoded config key (e.g. "Census" or "BDM"), build the default field
  // list, persist it to the DB, and update the local state in one pass.
  if ((!sp.fields || !sp.fields.length) && sp.id != null) {
    const defaults = _buildDefaultFieldsFromConfig(sp.name);
    if (defaults.length) {
      if (isProjectKindsProtected()) {
        // Viewing must stay password-free; persist defaults on the next explicit save.
        sp = Object.assign({}, sp, { fields: defaults });
      } else {
        try {
          const r = await api(`/api/sub-projects/${sp.id}`, {
            method: "PUT",
            body: JSON.stringify({ fields: defaults }),
          });
          if (r && r.ok && r.sub_project) {
            sp = r.sub_project;
            state.subProjects = (state.subProjects || []).map((p) =>
              Number(p.id) === Number(sp.id) ? sp : p
            );
          }
        } catch (_) {
          // Non-fatal: display with in-memory defaults even if save failed.
          sp = Object.assign({}, sp, { fields: defaults });
        }
      }
    }
  }

  subProfilePanelState.subProject = sp;
  subProfilePanelState.thresholdEditMode = false;
  subProfilePanelState.fieldSearchQuery = "";
  subProfilePanelState.editSnapshot = null;
  subProfilePanelState.pendingDocTypeAddFocus = null;
  const sInpReset = $("subProfileFieldSearch");
  if (sInpReset instanceof HTMLInputElement) sInpReset.value = "";
  _profilePanelUpdateEditChrome();
  _renderProfilePanel(sp);
  activateSubProjectProfileTab("overview");
  const backdrop = $("subProfileBackdrop");
  const panel = $("subProfilePanel");
  if (backdrop) backdrop.hidden = false;
  if (panel) { panel.hidden = false; panel.focus(); }
}


export function closeProfilePanel() {
  subProfilePanelState.subProject = null;
  subProfilePanelState.thresholdEditMode = false;
  subProfilePanelState.fieldSearchQuery = "";
  subProfilePanelState.editSnapshot = null;
  subProfilePanelState.pendingDocTypeAddFocus = null;
  const sInp = $("subProfileFieldSearch");
  if (sInp instanceof HTMLInputElement) sInp.value = "";
  _profilePanelUpdateEditChrome();
  const backdrop = $("subProfileBackdrop");
  const panel = $("subProfilePanel");
  if (backdrop) backdrop.hidden = true;
  if (panel) panel.hidden = true;
}


export async function saveProfilePanelThresholds() {
  const sp = subProfilePanelState.subProject;
  if (!sp || sp.id == null || !subProfilePanelState.thresholdEditMode) return;
  const nameInput = $("subProfileTitleInput");
  const newName =
    nameInput instanceof HTMLInputElement
      ? String(nameInput.value || "").trim()
      : String(sp.name || "").trim();
  const languageInput = $("subProfileLanguageInput");
  const newLanguage =
    languageInput instanceof HTMLSelectElement
      ? String(languageInput.value || "").trim()
      : String(sp.language || "").trim();
  const integralInput = $("subProfileIntegralKeyingInput");
  const newIntegralKeying =
    integralInput instanceof HTMLInputElement
      ? Boolean(integralInput.checked)
      : Boolean(sp.integral_keying);
  if (!newName) {
    setStatus("Le nom du sous-projet est requis.", true);
    nameInput?.focus?.();
    return;
  }
  const fieldsIn = Array.isArray(sp.fields) ? sp.fields : [];
  const fields = JSON.parse(JSON.stringify(fieldsIn));
  const byName = /** @type {Record<string, object>} */ (
    Object.fromEntries(fields.map((f) => [String(f.field_name || ""), f]))
  );
  document.querySelectorAll("#subProfileFieldsBody .sub-profile-fields-threshold-input").forEach((inp) => {
    if (!(inp instanceof HTMLInputElement)) return;
    const name = String(inp.dataset.fieldName || "").trim();
    if (!name || !byName[name]) return;
    const raw = String(inp.value).trim();
    if (raw === "") {
      byName[name].rekey_threshold = null;
    } else {
      const n = parseInt(raw, 10);
      if (isFinite(n)) {
        byName[name].rekey_threshold = Math.max(0, Math.min(100, n));
      }
    }
  });
  const body = {
    name: newName,
    root_kind: sp.root_kind,
    language: newLanguage,
    integral_keying: newIntegralKeying,
    doc_types: Array.isArray(sp.doc_types) ? sp.doc_types : [],
    fields,
    doctype_fields:
      sp.doctype_fields && typeof sp.doctype_fields === "object" ? sp.doctype_fields : {},
    header_key_only: Array.isArray(sp.header_key_only) ? sp.header_key_only : [],
    keying_instructions: Array.isArray(sp.keying_instructions) ? sp.keying_instructions : [],
  };
  const chefPassword = await promptChefPasswordForAction(
    "Saisissez le mot de passe Chef de projet pour enregistrer les modifications."
  );
  if (chefPassword === null) return;
  try {
    const r = await api(`/api/sub-projects/${Number(sp.id)}`, {
      method: "PUT",
      body: JSON.stringify(withChefPassword(body, chefPassword)),
    });
    const updated = r && r.ok && r.sub_project ? r.sub_project : null;
    const nextSp = updated || Object.assign({}, sp, { fields });
    subProfilePanelState.subProject = nextSp;
    state.subProjects = (state.subProjects || []).map((p) =>
      Number(p.id) === Number(nextSp.id) ? nextSp : p
    );
    renderSubProjects(state.subProjects);
    renderFieldsKindTabs();
    await refreshActiveBatchDocTypeConfiguration();
    subProfilePanelState.thresholdEditMode = false;
    subProfilePanelState.fieldSearchQuery = "";
    subProfilePanelState.editSnapshot = null;
    const sInp = $("subProfileFieldSearch");
    if (sInp instanceof HTMLInputElement) sInp.value = "";
    _profilePanelUpdateEditChrome();
    _renderProfilePanel(subProfilePanelState.subProject);
    setStatus("Modifications enregistr\u00e9es.");
  } catch (e) {
    setStatus(String(e && e.message ? e.message : e), true);
  }
}


export function initProfilePanel() {
  initSubProjectCiProfileTabs();
  $("btnProfileClose")?.addEventListener("click", () => closeProfilePanel());
  $("subProfileBackdrop")?.addEventListener("click", () => closeProfilePanel());

  $("btnProfileEdit")?.addEventListener("click", () => {
    const sp = subProfilePanelState.subProject;
    if (!sp) return;
    if (subProfilePanelState.thresholdEditMode) {
      cancelProfilePanelThresholdEdit();
      return;
    }
    subProfilePanelState.thresholdEditMode = true;
    subProfilePanelState.fieldSearchQuery = "";
    subProfilePanelState.editSnapshot = {
      name: sp.name,
      language: sp.language,
      integral_keying: Boolean(sp.integral_keying),
      fields: JSON.parse(JSON.stringify(Array.isArray(sp.fields) ? sp.fields : [])),
      keying_instructions: JSON.parse(
        JSON.stringify(Array.isArray(sp.keying_instructions) ? sp.keying_instructions : [])
      ),
      doc_types: JSON.parse(JSON.stringify(Array.isArray(sp.doc_types) ? sp.doc_types : [])),
      header_key_only: JSON.parse(
        JSON.stringify(Array.isArray(sp.header_key_only) ? sp.header_key_only : [])
      ),
      doctype_fields: JSON.parse(
        JSON.stringify(
          sp.doctype_fields && typeof sp.doctype_fields === "object" ? sp.doctype_fields : {}
        )
      ),
    };
    const sInp = $("subProfileFieldSearch");
    if (sInp instanceof HTMLInputElement) sInp.value = "";
    _profilePanelUpdateEditChrome();
    _renderProfilePanel(sp);
    requestAnimationFrame(() => $("subProfileTitleInput")?.focus?.());
  });

  $("btnProfileThresholdSave")?.addEventListener("click", () => {
    void saveProfilePanelThresholds();
  });

  $("subProfileFieldSearch")?.addEventListener("input", (ev) => {
    if (!(ev.target instanceof HTMLInputElement)) return;
    subProfilePanelState.fieldSearchQuery = ev.target.value;
    _applySubProfileFieldSearchFilter();
  });

  $("btnProfileDelete")?.addEventListener("click", () => {
    const sp = subProfilePanelState.subProject;
    if (!sp) return;
    void deleteSubProjectById(sp.id, sp.name);
  });

  document.addEventListener("keydown", (e) => {
    if (e.key !== "Escape" || !subProfilePanelState.subProject) return;
    if (runtime.subProjectDeleteConfirmResolver) return;
    if (subProfilePanelState.thresholdEditMode) {
      e.preventDefault();
      cancelProfilePanelThresholdEdit();
      return;
    }
    closeProfilePanel();
  });
}
