import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
import { setValue, isPlainObject, getValue, normalize, flatten, isPrefix } from './formdata.mjs';
import { invariant } from './util.mjs';

/**
 * The name to be used when submitting a form control
 */
var INTENT = '__intent__';

/**
 * The name to be used when submitting a state
 */
var STATE = '__state__';
function getSubmissionContext(body) {
  var intent = body.get(INTENT);
  var state = body.get(STATE);
  invariant((typeof intent === 'string' || intent === null) && (typeof state === 'string' || state === null), "The input name \"".concat(INTENT, "\" and \"").concat(STATE, "\" are reserved by Conform. Please use another name for your input."));
  var context = {
    payload: {},
    fields: new Set(),
    intent: getIntent(intent)
  };
  if (state) {
    context.state = JSON.parse(state);
  }
  var _loop = function _loop(next) {
    if (name === INTENT || name === STATE) {
      return 1; // continue
    }
    context.fields.add(name);
    setValue(context.payload, name, prev => {
      if (!prev) {
        return next;
      } else if (Array.isArray(prev)) {
        return prev.concat(next);
      } else {
        return [prev, next];
      }
    });
  };
  for (var [name, next] of body.entries()) {
    if (_loop(next)) continue;
  }
  return context;
}
function parse(payload, options) {
  var context = getSubmissionContext(payload);
  var intent = context.intent;
  if (intent) {
    switch (intent.type) {
      case 'update':
        {
          var {
            name
          } = intent.payload;
          var _value = serialize(intent.payload.value);
          if (typeof _value !== 'undefined') {
            if (name) {
              setValue(context.payload, name, () => _value);
            } else {
              // @ts-expect-error FIXME - it must be an object if there is no name
              context.payload = _value;
            }
          }
          break;
        }
      case 'reset':
        {
          var {
            name: _name
          } = intent.payload;
          if (_name) {
            setValue(context.payload, _name, () => undefined);
          } else {
            context.payload = {};
          }
          break;
        }
      case 'insert':
      case 'remove':
      case 'reorder':
        {
          setListValue(context.payload, intent);
          break;
        }
    }
  }
  var result = options.resolve(context.payload, intent);
  var mergeResolveResult = resolved => createSubmission(_objectSpread2(_objectSpread2({}, context), {}, {
    value: resolved.value,
    error: resolved.error
  }));
  if (result instanceof Promise) {
    return result.then(mergeResolveResult);
  }
  return mergeResolveResult(result);
}
function createSubmission(context) {
  if (context.intent || !context.value || context.error) {
    return {
      status: !context.intent ? 'error' : undefined,
      payload: context.payload,
      error: typeof context.error !== 'undefined' ? context.error : {},
      reply(options) {
        return replySubmission(context, options);
      }
    };
  }
  return {
    status: 'success',
    payload: context.payload,
    value: context.value,
    reply(options) {
      return replySubmission(context, options);
    }
  };
}
function replySubmission(context) {
  var _context$intent, _context$intent$paylo, _options$formErrors, _normalize;
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  if ('resetForm' in options && options.resetForm || ((_context$intent = context.intent) === null || _context$intent === void 0 ? void 0 : _context$intent.type) === 'reset' && ((_context$intent$paylo = context.intent.payload.name) !== null && _context$intent$paylo !== void 0 ? _context$intent$paylo : '') === '') {
    return {
      initialValue: null
    };
  }
  if ('hideFields' in options && options.hideFields) {
    for (var name of options.hideFields) {
      var _value2 = getValue(context.payload, name);
      if (typeof _value2 !== 'undefined') {
        setValue(context.payload, name, () => undefined);
      }
    }
  }
  var extraError = 'formErrors' in options || 'fieldErrors' in options ? normalize(_objectSpread2({
    '': (_options$formErrors = options.formErrors) !== null && _options$formErrors !== void 0 ? _options$formErrors : null
  }, options.fieldErrors)) : null;
  var error = context.error || extraError ? _objectSpread2(_objectSpread2({}, context.error), extraError) : undefined;
  return {
    status: context.intent ? undefined : error ? 'error' : 'success',
    intent: context.intent ? context.intent : undefined,
    initialValue: (_normalize = normalize(context.payload,
    // We can't serialize the file and send it back from the server, but we can preserve it in the client
    typeof document !== 'undefined')) !== null && _normalize !== void 0 ? _normalize : {},
    error,
    state: context.state,
    fields: Array.from(context.fields)
  };
}
function getIntent(serializedIntent) {
  if (!serializedIntent) {
    return null;
  }
  var control = JSON.parse(serializedIntent);
  if (typeof control.type !== 'string' || typeof control.payload === 'undefined') {
    throw new Error('Unknown form control intent');
  }
  return control;
}
function serializeIntent(intent) {
  return JSON.stringify(intent);
}
function updateList(list, intent) {
  var _intent$payload$index;
  invariant(Array.isArray(list), "Failed to update list. The value is not an array.");
  switch (intent.type) {
    case 'insert':
      list.splice((_intent$payload$index = intent.payload.index) !== null && _intent$payload$index !== void 0 ? _intent$payload$index : list.length, 0, serialize(intent.payload.defaultValue));
      break;
    case 'remove':
      list.splice(intent.payload.index, 1);
      break;
    case 'reorder':
      list.splice(intent.payload.to, 0, ...list.splice(intent.payload.from, 1));
      break;
    default:
      throw new Error('Unknown list intent received');
  }
}
function setListValue(data, intent) {
  setValue(data, intent.payload.name, value => {
    var list = value !== null && value !== void 0 ? value : [];
    updateList(list, intent);
    return list;
  });
}
function setState(state, name, valueFn) {
  var root = Symbol.for('root');

  // The keys are sorted in desc so that the root value is handled last
  var keys = Object.keys(state).sort((prev, next) => next.localeCompare(prev));
  var target = {};
  var _loop2 = function _loop2() {
    var value = state[key];
    if (isPrefix(key, name) && key !== name) {
      setValue(target, key, currentValue => {
        if (typeof currentValue === 'undefined') {
          return value;
        }

        // As the key should be unique, if currentValue is already defined,
        // it must be either an object or an array

        // @ts-expect-error
        currentValue[root] = value;
        return currentValue;
      });

      // Remove the value from the data
      delete state[key];
    }
  };
  for (var key of keys) {
    _loop2();
  }
  var result = valueFn(getValue(target, name));
  Object.assign(state,
  // @ts-expect-error FIXME flatten should be more flexible
  flatten(result, {
    resolve(data) {
      if (isPlainObject(data) || Array.isArray(data)) {
        var _data$root;
        // @ts-expect-error
        return (_data$root = data[root]) !== null && _data$root !== void 0 ? _data$root : null;
      }
      return data;
    },
    prefix: name
  }));
}
function setListState(state, intent, getDefaultValue) {
  setState(state, intent.payload.name, value => {
    var list = value !== null && value !== void 0 ? value : [];
    switch (intent.type) {
      case 'insert':
        updateList(list, {
          type: intent.type,
          payload: _objectSpread2(_objectSpread2({}, intent.payload), {}, {
            defaultValue: getDefaultValue === null || getDefaultValue === void 0 ? void 0 : getDefaultValue()
          })
        });
        break;
      default:
        updateList(list, intent);
        break;
    }
    return list;
  });
}
function serialize(defaultValue) {
  if (isPlainObject(defaultValue)) {
    // @ts-expect-error FIXME
    return Object.entries(defaultValue).reduce((result, _ref) => {
      var [key, value] = _ref;
      // @ts-ignore-error FIXME
      result[key] = serialize(value);
      return result;
    }, {});
  } else if (Array.isArray(defaultValue)) {
    // @ts-expect-error FIXME
    return defaultValue.map(serialize);
  } else if (
  // @ts-ignore-error FIXME
  defaultValue instanceof Date) {
    // @ts-expect-error FIXME
    return defaultValue.toISOString();
  } else if (typeof defaultValue === 'boolean') {
    // @ts-expect-error FIXME
    return defaultValue ? 'on' : undefined;
  } else if (typeof defaultValue === 'number') {
    // @ts-expect-error FIXME
    return defaultValue.toString();
  } else {
    // @ts-expect-error FIXME
    return defaultValue !== null && defaultValue !== void 0 ? defaultValue : undefined;
  }
}

export { INTENT, STATE, createSubmission, getIntent, getSubmissionContext, parse, replySubmission, serialize, serializeIntent, setListState, setListValue, setState, updateList };
