import { $ } from '../core/dom.js';
import { getCurrentRootKind, normalizeDocTypeKey, normalizeEventTypeValue } from './project-context.js';
import { state } from '../state/app-state.js';
import { showConfirmActionPanel } from '../ui/modals.js';

/** @type {Set<string>} */
const dismissedMixedPromptKeys = new Set();

/** BDM Event Types that share one DocType family (not a Mixed mismatch). */
const BDM_EVENT_TYPE_FAMILIES = {
  birth: new Set(["Birth", "Baptism"]),
  marriage: new Set(["Marriage", "Banns"]),
  death: new Set(["Burial", "Death"]),
};

/**
 * Resolve the BDM DocType family for one Event Type value.
 *
 * @param {string} eventType
 * @returns {string}
 */
export function resolveBdmEventTypeFamily(eventType) {
  const normalized = normalizeEventTypeValue(eventType);
  if (!normalized) return "";
  const entries = Object.entries(BDM_EVENT_TYPE_FAMILIES);
  for (let i = 0; i < entries.length; i += 1) {
    const [family, members] = entries[i];
    if (members.has(normalized)) return family;
  }
  return normalized.toLowerCase();
}

/**
 * True when distinct Event Types span more than one BDM family (real Mixed mismatch).
 *
 * @param {string[]} eventTypes
 * @returns {boolean}
 */
export function hasCrossFamilyBdmEventTypeMismatch(eventTypes) {
  if (!Array.isArray(eventTypes) || eventTypes.length < 2) return false;
  /** @type {Set<string>} */
  const families = new Set();
  for (let i = 0; i < eventTypes.length; i += 1) {
    const family = resolveBdmEventTypeFamily(eventTypes[i]);
    if (!family) continue;
    families.add(family);
  }
  return families.size >= 2;
}

/**
 * Build a stable session key for mixed-doctype prompt dismissal.
 *
 * @param {string} batchName
 * @param {number|string} imageId
 * @returns {string}
 */
export function buildMixedDocTypePromptKey(batchName, imageId) {
  return `${String(batchName || "").trim()}|${String(imageId)}`;
}

/**
 * Collect distinct non-empty Event Type values from record RecordType.orig fields.
 *
 * @param {object|null|undefined} work
 * @returns {string[]}
 */
export function collectDistinctRecordEventTypes(work) {
  const records = Array.isArray(work && work.records) ? work.records : [];
  /** @type {Set<string>} */
  const types = new Set();
  for (let i = 0; i < records.length; i += 1) {
    const rec = records[i];
    const fields = (rec && rec.fields) || {};
    const recordType = fields.RecordType;
    const raw = recordType && recordType.orig != null ? recordType.orig : "";
    const normalized = normalizeEventTypeValue(raw);
    if (normalized) types.add(normalized);
  }
  return Array.from(types).sort((a, b) => a.localeCompare(b, "fr", { sensitivity: "base" }));
}

/**
 * Resolve the batch DocType label that maps to Mixed.
 *
 * @returns {string}
 */
export function resolveMixedDocTypeLabel() {
  const sel = $("docTypeSelect");
  if (sel) {
    const options = Array.from(sel.options || []);
    for (let i = 0; i < options.length; i += 1) {
      const value = String(options[i].value || "").trim();
      if (normalizeDocTypeKey(value) === "mixed") return value;
    }
  }
  const groups = state.batchDocTypeGroups;
  const names = [
    ...(Array.isArray(groups && groups.data) ? groups.data : []),
    ...(Array.isArray(groups && groups.non_data) ? groups.non_data : []),
  ];
  for (let i = 0; i < names.length; i += 1) {
    const name = String(names[i] || "").trim();
    if (normalizeDocTypeKey(name) === "mixed") return name;
  }
  return "Mixed";
}

/**
 * Analyze whether a BDM image should offer switching to Mixed DocType.
 *
 * @param {object|null|undefined} work
 * @param {string} batchName
 * @param {{ bypassDismiss?: boolean }|undefined} [options]
 * @returns {{ eventTypes: string[], mixedDocType: string }|null}
 */
export function analyzeMixedDocTypeSuggestion(work, batchName, options) {
  if (!work || !batchName) return null;
  if (String(getCurrentRootKind() || "").toUpperCase() !== "BDM") return null;

  const currentDocType = String(work.form_type || state.docType || "").trim();
  if (!currentDocType || normalizeDocTypeKey(currentDocType) === "mixed") return null;

  const records = Array.isArray(work.records) ? work.records : [];
  if (records.length < 2) return null;

  const eventTypes = collectDistinctRecordEventTypes(work);
  if (eventTypes.length < 2) return null;
  if (!hasCrossFamilyBdmEventTypeMismatch(eventTypes)) return null;

  const mixedDocType = resolveMixedDocTypeLabel();
  if (normalizeDocTypeKey(mixedDocType) !== "mixed") return null;

  const bypassDismiss = Boolean(options && options.bypassDismiss);
  const dismissKey = buildMixedDocTypePromptKey(batchName, work.image_id);
  if (!bypassDismiss && dismissedMixedPromptKeys.has(dismissKey)) return null;

  return { eventTypes, mixedDocType };
}

/**
 * Prompt the operator to switch the current image DocType to Mixed when records
 * carry multiple Event Types. Returns the Mixed DocType label when accepted.
 *
 * @param {object|null|undefined} work
 * @param {number|string} imageId
 * @param {string} batchName
 * @param {{ bypassDismiss?: boolean }|undefined} [options]
 * @returns {Promise<string|null>}
 */
export async function maybePromptMixedDocTypeOnLoad(work, imageId, batchName, options) {
  const suggestion = analyzeMixedDocTypeSuggestion(work, batchName, options);
  if (!suggestion) return null;

  const dismissKey = buildMixedDocTypePromptKey(batchName, imageId);
  const currentLabel = String((work && work.form_type) || state.docType || "").trim() || "(vide)";
  const typesLabel = suggestion.eventTypes.join(", ");
  const message =
    "Cette image contient des enregistrements avec des Event Types incompatibles : "
    + typesLabel
    + ".\n\nDocType actuel : "
    + currentLabel
    + "\n\nSouhaitez-vous passer le DocType de cette image à Mixed ?";

  const ok = await showConfirmActionPanel(
    "Event Types incompatibles détectés",
    message,
    "Passer à Mixed",
    { cancelLabel: "Garder le DocType actuel", kind: "default" }
  );

  if (!ok) {
    dismissedMixedPromptKeys.add(dismissKey);
    return null;
  }
  if (state.batch !== batchName) return null;
  if (!state.work || Number(state.work.image_id) !== Number(imageId)) return null;

  return suggestion.mixedDocType;
}
