/** Effektives Ablaufprofil für Kombination im Coach/in der Planung */

import { inferAdvanceModeFromStoredSlotRow } from './combinationMethodProfileUi'

/** Top-Level: null/undefined aus Planungs-Snapshot löschen keine Katalog-Felder (API liefert oft JSON-null). */
function omitNullUndefinedTop(planObj) {
  const out = {}
  for (const [k, v] of Object.entries(planObj)) {
    if (v === null || v === undefined) continue
    out[k] = v
  }
  return out
}

/** Je Slot-Zeile: null/undefined aus Planung überschreiben keine Katalog-Werte (z. B. consecutive_reps). */
function patchHasExplicitAdvanceMode(patch) {
  return (
    Object.prototype.hasOwnProperty.call(patch, 'advance_mode') &&
    patch.advance_mode !== null &&
    patch.advance_mode !== undefined &&
    String(patch.advance_mode).trim() !== ''
  )
}

/** Nach Merge: Rep/Manual ohne Sekunden-Arbeit; Zeit ohne advance_mode-Schlüssel. */
function coerceMergedSlotProfileRow(row) {
  if (!row || typeof row !== 'object') return row
  const inferred = inferAdvanceModeFromStoredSlotRow(row)
  if (inferred === 'rep' || inferred === 'manual') {
    delete row.load_sec
    row.advance_mode = inferred
  } else {
    delete row.advance_mode
  }
  return row
}

function mergeSlotProfileFields(prev, patch) {
  const base = prev && typeof prev === 'object' ? { ...prev } : {}
  if (!patch || typeof patch !== 'object') return coerceMergedSlotProfileRow(base)

  for (const [k, v] of Object.entries(patch)) {
    if (v === null || v === undefined) continue
    base[k] = v
  }

  const loadExplicit =
    Object.prototype.hasOwnProperty.call(patch, 'load_sec') &&
    patch.load_sec !== null &&
    patch.load_sec !== undefined &&
    String(patch.load_sec).trim() !== ''

  // Nur Arbeitsssekunden aus Planung → Zeitmodus soll Katalog‑advance_mode rep nicht „festhalten“
  if (loadExplicit && !patchHasExplicitAdvanceMode(patch)) {
    delete base.advance_mode
  }

  const ix = Number(base.slot_index)
  if (Number.isFinite(ix)) base.slot_index = ix

  return coerceMergedSlotProfileRow(base)
}

/**
 * Vereinigt slot_profiles_v1 aus Katalog und Planungs-Overlay (je slot_index).
 * @param {unknown} catArr
 * @param {unknown} planArr
 */
function mergeSlotProfilesV1(catArr, planArr) {
  const c = Array.isArray(catArr) ? catArr : []
  const p = Array.isArray(planArr) ? planArr : []
  const byIx = new Map()
  for (const r of c) {
    if (!r || typeof r !== 'object') continue
    const ix = Number(r.slot_index)
    if (!Number.isFinite(ix)) continue
    byIx.set(ix, { ...r })
  }
  for (const r of p) {
    if (!r || typeof r !== 'object') continue
    const ix = Number(r.slot_index)
    if (!Number.isFinite(ix)) continue
    const prev = byIx.get(ix) || {}
    byIx.set(ix, mergeSlotProfileFields(prev, r))
  }
  return [...byIx.entries()].sort((a, b) => a[0] - b[0]).map(([, row]) => row)
}

/**
 * Katalog-Basis + optionaler Planungs-Snapshot.
 * Wichtig: `planning_method_profile` darf nur **Überschreibungen** sein — nie den Katalog komplett ersetzen
 * (sonst verschwinden Zeiten/Runden bei leerem Objekt oder Teil-Payload).
 */
export function effectiveComboMethodProfile(catalogDict, planningSnapshot) {
  const cat =
    catalogDict && typeof catalogDict === 'object' && !Array.isArray(catalogDict) ? { ...catalogDict } : {}

  if (planningSnapshot === null || planningSnapshot === undefined) {
    return { ...cat }
  }

  if (typeof planningSnapshot === 'string') {
    const t = planningSnapshot.trim()
    if (!t || t === 'null') return { ...cat }
    try {
      const p = JSON.parse(t)
      return effectiveComboMethodProfile(catalogDict, p)
    } catch {
      return { ...cat }
    }
  }

  if (typeof planningSnapshot !== 'object' || Array.isArray(planningSnapshot)) {
    return { ...cat }
  }

  const planRaw = planningSnapshot
  const merged = { ...cat, ...omitNullUndefinedTop(planRaw) }

  if (Object.prototype.hasOwnProperty.call(planRaw, 'slot_profiles_v1')) {
    merged.slot_profiles_v1 = mergeSlotProfilesV1(cat.slot_profiles_v1, planRaw.slot_profiles_v1)
  }

  return merged
}

export function comboPlanningProfileJsonForEditor(catalogDict, planningSnapshot) {
  const o = effectiveComboMethodProfile(catalogDict, planningSnapshot)
  try {
    return JSON.stringify(Object.keys(o).length ? o : {})
  } catch {
    return '{}'
  }
}
