(function initFivaRuntimeStore(global) {
  "use strict";

  if (global.FIVA?.FivaRuntimeStore) return;

  const MAX_ACTIVITY = 8;
  const UPLOAD_STATUS = Object.freeze({
    EMPTY: "EMPTY",
    UPLOADING: "UPLOADING",
    PARSED: "PARSED",
    PASTED_TEXT_PARSED: "PASTED_TEXT_PARSED",
    FAILED: "FAILED"
  });
  const PARSER_STATUS = Object.freeze({
    IDLE: "IDLE",
    RUNNING: "RUNNING",
    SUCCESS: "SUCCESS",
    FAILED: "FAILED"
  });

  function now() {
    return Date.now();
  }

  function stateMachine() {
    return global.FIVA?.FivaStateMachine || {
      STATES: { IDLE: "IDLE", READY: "READY", FAILED: "FAILED" },
      createRuntime: (patch = {}) => ({
        state: patch.state || "IDLE",
        status: patch.status || patch.state || "READY",
        failedFields: Array.isArray(patch.failedFields) ? patch.failedFields : [],
        active: false,
        updatedAt: now()
      }),
      nextRuntime: (current = {}, patch = {}) => ({
        ...current,
        ...patch,
        updatedAt: now()
      })
    };
  }

  function cleanText(value = "") {
    return String(value || "").replace(/\s+/g, " ").trim();
  }

  function hasProfileData(candidate = null) {
    const profile = candidate?.profile || candidate || {};
    return Object.entries(profile).some(([key, value]) => (
      !String(key).startsWith("__") && cleanText(value)
    ));
  }

  function normalizeActivity(items = []) {
    return (Array.isArray(items) ? items : [])
      .filter((item) => cleanText(item?.message))
      .map((item) => ({
        message: cleanText(item.message),
        level: item.level === "warn" || item.level === "error" ? item.level : "info",
        at: Number(item.at || now())
      }))
      .slice(0, MAX_ACTIVITY);
  }

  function isUploadRequiredMessage(message = "") {
    return /upload.*document.*first|upload.*analyze|missing document|document parsing failed|parser failed|could not analyze document|no valid application data found/i
      .test(cleanText(message));
  }

  function removeUploadWarnings(items = []) {
    return normalizeActivity(items).filter((item) => !isUploadRequiredMessage(item.message));
  }

  function uploadedFileFromProfile(parsedProfile = null) {
    const source = parsedProfile?.source || {};
    const name = cleanText(source.fileName);
    if (!name) return null;
    return {
      name,
      type: cleanText(source.type),
      size: Number(source.fileSize || 0),
      lastModified: Number(source.fileLastModified || 0)
    };
  }

  function parsedUploadStatus(parsedProfile = null, fallback = "") {
    if (fallback === UPLOAD_STATUS.PASTED_TEXT_PARSED) return UPLOAD_STATUS.PASTED_TEXT_PARSED;
    return parsedProfile?.source?.type === "pasted_text" ? UPLOAD_STATUS.PASTED_TEXT_PARSED : UPLOAD_STATUS.PARSED;
  }

  function isReadyUploadStatus(status = "") {
    return status === UPLOAD_STATUS.PARSED || status === UPLOAD_STATUS.PASTED_TEXT_PARSED;
  }

  function createStore(patch = {}) {
    const machine = stateMachine();
    const parsedProfile = patch.parsedProfile || null;
    const hasData = hasProfileData(parsedProfile);
    const runtimeState = machine.createRuntime(patch.runtimeState || {
      state: patch.state || (hasData ? machine.STATES.READY : machine.STATES.IDLE),
      status: patch.status || "READY",
      failedFields: patch.failedFields || []
    });
    const parserStatus = cleanText(patch.parserStatus || (hasData ? PARSER_STATUS.SUCCESS : PARSER_STATUS.IDLE));
    const uploadStatus = cleanText(patch.uploadStatus || (hasData ? UPLOAD_STATUS.PARSED : UPLOAD_STATUS.EMPTY));
    const ready = patch.ready === true || (hasData && parserStatus === PARSER_STATUS.SUCCESS && isReadyUploadStatus(uploadStatus));

    return validateFivaRuntimeState({
      uploadId: cleanText(patch.uploadId || parsedProfile?.source?.uploadId || ""),
      uploadedFile: patch.uploadedFile || uploadedFileFromProfile(parsedProfile),
      parsedProfile,
      uploadStatus,
      parserStatus,
      autofillStatus: cleanText(patch.autofillStatus || runtimeState.status || "READY"),
      ready,
      active: patch.active === true || runtimeState.active === true,
      lastError: patch.lastError ? cleanText(patch.lastError) : null,
      runtimeState,
      queue: Array.isArray(patch.queue) ? patch.queue.slice() : [],
      lastActivity: normalizeActivity(patch.lastActivity),
      startedAt: patch.startedAt || null,
      updatedAt: Number(patch.updatedAt || now())
    });
  }

  function validateFivaRuntimeState(store = {}) {
    const machine = stateMachine();
    const hasData = hasProfileData(store.parsedProfile);
    const next = { ...store };

    if (next.ready && !hasData) {
      next.ready = false;
      next.uploadStatus = UPLOAD_STATUS.EMPTY;
      next.parserStatus = PARSER_STATUS.IDLE;
      next.parsedProfile = null;
      next.runtimeState = machine.nextRuntime(next.runtimeState, { state: machine.STATES.IDLE, status: "READY", failedFields: [] });
    }

    if (hasData && next.parserStatus === PARSER_STATUS.SUCCESS) {
      next.uploadStatus = parsedUploadStatus(next.parsedProfile, next.uploadStatus);
      next.ready = true;
      next.lastError = null;
      next.lastActivity = removeUploadWarnings(next.lastActivity);
      if (next.runtimeState?.state === machine.STATES.FAILED && isUploadRequiredMessage(next.lastError || next.runtimeState?.status)) {
        next.runtimeState = machine.nextRuntime(next.runtimeState, { state: machine.STATES.READY, status: "READY", failedFields: [] });
      }
    }

    if (next.ready && next.parserStatus !== PARSER_STATUS.SUCCESS) {
      next.parserStatus = PARSER_STATUS.SUCCESS;
    }

    if (next.ready && !isReadyUploadStatus(next.uploadStatus)) {
      next.uploadStatus = parsedUploadStatus(next.parsedProfile, next.uploadStatus);
    }

    if (isReadyUploadStatus(next.uploadStatus) && next.parserStatus === PARSER_STATUS.FAILED) {
      next.parserStatus = hasData ? PARSER_STATUS.SUCCESS : PARSER_STATUS.FAILED;
      next.ready = hasData;
    }

    next.active = next.runtimeState?.active === true || next.active === true;
    return next;
  }

  function nextStore(current = {}, patch = {}) {
    const machine = stateMachine();
    const base = createStore(current);
    const parsedProfile = Object.hasOwn(patch, "parsedProfile") ? patch.parsedProfile : base.parsedProfile;
    const runtimePatch = Object.hasOwn(patch, "runtimeState") ? patch.runtimeState : null;
    const runtimeState = runtimePatch
      ? machine.nextRuntime(base.runtimeState, runtimePatch)
      : base.runtimeState;
    const store = createStore({
      ...base,
      ...patch,
      parsedProfile,
      runtimeState,
      uploadId: Object.hasOwn(patch, "uploadId") ? patch.uploadId : (parsedProfile?.source?.uploadId || base.uploadId),
      uploadedFile: Object.hasOwn(patch, "uploadedFile") ? patch.uploadedFile : (uploadedFileFromProfile(parsedProfile) || base.uploadedFile),
      lastActivity: Object.hasOwn(patch, "lastActivity") ? patch.lastActivity : base.lastActivity,
      updatedAt: now()
    });
    return store;
  }

  function withRuntime(current = {}, runtimePatch = {}) {
    const nextRuntime = stateMachine().nextRuntime(createStore(current).runtimeState, runtimePatch);
    return nextStore(current, {
      runtimeState: nextRuntime,
      autofillStatus: nextRuntime.status || runtimePatch.status || createStore(current).autofillStatus,
      active: nextRuntime.active === true,
      lastError: runtimePatch.state === stateMachine().STATES.FAILED ? runtimePatch.status || createStore(current).lastError : createStore(current).lastError
    });
  }

  function withProfile(current = {}, parsedProfile = null) {
    const hasData = hasProfileData(parsedProfile);
    const machine = stateMachine();
    return nextStore(current, {
      uploadId: parsedProfile?.source?.uploadId || "",
      uploadedFile: uploadedFileFromProfile(parsedProfile),
      parsedProfile: hasData ? parsedProfile : null,
      uploadStatus: hasData ? parsedUploadStatus(parsedProfile) : UPLOAD_STATUS.FAILED,
      parserStatus: hasData ? PARSER_STATUS.SUCCESS : PARSER_STATUS.FAILED,
      autofillStatus: hasData ? "READY" : "FAILED",
      ready: hasData,
      active: false,
      lastError: hasData ? null : "Document parsing failed",
      queue: [],
      startedAt: null,
      runtimeState: {
        state: hasData ? machine.STATES.READY : machine.STATES.FAILED,
        status: hasData ? "READY" : "FAILED",
        failedFields: []
      }
    });
  }

  function addActivity(current = {}, message = "", level = "info", runtimePatch = {}) {
    const text = cleanText(message);
    if (!text) return createStore(current);
    const base = createStore(current);
    if (base.ready && isUploadRequiredMessage(text)) {
      return nextStore(base, { lastActivity: removeUploadWarnings(base.lastActivity), lastError: null });
    }
    const previous = base.lastActivity || [];
    const activity = previous[0]?.message === text && previous[0]?.level === level
      ? previous.slice(0, MAX_ACTIVITY)
      : normalizeActivity([{ message: text, level, at: now() }, ...previous]);
    const next = Object.keys(runtimePatch || {}).length ? withRuntime(base, runtimePatch) : base;
    return nextStore(next, { lastActivity: activity });
  }

  function reset() {
    return createStore();
  }

  const FivaRuntimeStore = Object.freeze({
    MAX_ACTIVITY,
    PARSER_STATUS,
    UPLOAD_STATUS,
    addActivity,
    createStore,
    hasProfileData,
    isUploadRequiredMessage,
    isReadyUploadStatus,
    nextStore,
    normalizeActivity,
    removeUploadWarnings,
    reset,
    parsedUploadStatus,
    validateFivaRuntimeState,
    withProfile,
    withRuntime
  });

  global.FIVA = Object.assign(global.FIVA || {}, { FivaRuntimeStore });

  if (typeof module !== "undefined" && module.exports) {
    module.exports = FivaRuntimeStore;
  }
})(typeof globalThis !== "undefined" ? globalThis : this);
