<script setup>
import { cel as _rawCel } from "../../utils/cel";
import { TZDate } from "@date-fns/tz";
import { Effect } from "effect";
import { Fetch } from "fx-fetch";
import { computed } from "vue";
import {
  celBindings,
  injectCELContext,
  provideCELContext,
  provideScopeAddress
} from "../../utils/cel-context";
import FormUnitRenderer from "./FormUnitRenderer.vue";
import { buildFormRuntimeScope } from "./utils/cel-scope";
import { provideCommitBus } from "./utils/commit-bus";
import { provideDerivedQuiescence, useDerived } from "./utils/derived";
import { useFormHistory } from "./utils/history";
import { evaluateInitial } from "./utils/initial";
import { provideEventTarget } from "../../share/event-bus";
import { provideFormReadonly } from "./utils/readonly";
import { provideFormState } from "./utils/state";
import { provideSelectionsRegistry } from "../../utils/selections-registry";
import { provideValidation } from "./utils/validation";
defineOptions({ name: "ShwfedForm" });
const state = defineModel({ type: Object, ...{ default: () => ({}) } });
const config = defineModel("config", { type: Object, ...{ required: true } });
const props = defineProps({
  configure: { type: Function, required: false }
});
const configure = props.configure ?? (() => {
});
void configure;
const selections = provideSelectionsRegistry();
provideCELContext(buildFormRuntimeScope({
  form: () => state.value ?? {},
  now: () => new TZDate(),
  // `formHistory` is created further down; these thunks are stored now and
  // only called at eval time (long after setup), so reading it here is safe.
  undoable: () => formHistory.canUndo.value,
  redoable: () => formHistory.canRedo.value,
  selections: () => selections.entries.value
}));
if (config.value.id) {
  provideScopeAddress(config.value.id, {
    form: () => state.value ?? {},
    selections: () => selections.entries.value
  });
}
const inherited = injectCELContext();
const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
const formState = provideFormState(state);
const commitBus = provideCommitBus();
const quiescence = provideDerivedQuiescence();
const formReadonly = computed(() => {
  const expr = config.value.readonly;
  if (!expr) return false;
  try {
    return Effect.runSync($cel(expr, { form: state.value ?? {} }));
  } catch (err) {
    console.error("[shwfed-form] failed to evaluate form readonly:", err);
    return false;
  }
});
provideFormReadonly(formReadonly);
const seeded = (() => {
  const current = state.value ?? {};
  const initial = config.value.initial;
  if (!initial || Object.keys(current).length > 0) return Promise.resolve();
  const program = evaluateInitial(initial, $cel, { form: current });
  return Effect.runPromise(Effect.provide(program, Fetch.layer)).then(
    (result) => {
      if (result && typeof result === "object" && !Array.isArray(result)) {
        state.value = { ...current, ...result };
      }
    },
    (err) => {
      console.error("[shwfed-form] failed to evaluate initial:", err);
    }
  );
})();
useDerived({
  fields: () => config.value.fields,
  evaluate: (expression) => $cel(expression),
  ready: seeded,
  formState,
  quiescence
});
const validation = provideValidation({
  flush: commitBus.flushAll,
  seeded,
  settled: quiescence.settled
});
const formHistory = useFormHistory(state, { quiescence, seeded, dirty: formState.dirty });
class FormInvalid {
  _tag = "FormInvalid";
}
const formInstanceId = config.value?.id ?? crypto.randomUUID();
provideEventTarget(formInstanceId, {
  "validate": () => Effect.gen(function* () {
    const valid = yield* Effect.promise(() => validation.validate());
    if (!valid) yield* Effect.die(new FormInvalid());
  }),
  "undo": () => Effect.sync(formHistory.undo),
  "redo": () => Effect.sync(formHistory.redo),
  "reset-to-initial": () => Effect.sync(formHistory.reset)
});
defineExpose({
  seeded,
  flush: commitBus.flushAll,
  settled: quiescence.settled,
  validate: validation.validate,
  undo: formHistory.undo,
  redo: formHistory.redo,
  reset: formHistory.reset,
  canUndo: formHistory.canUndo,
  canRedo: formHistory.canRedo
});
const unit = computed(() => ({
  fields: config.value.fields,
  layouts: config.value.layouts
}));
function evaluateMedia(expression) {
  return Effect.runSync($cel(expression));
}
function isHidden(field) {
  const hidden = field.hidden;
  if (!hidden) return false;
  try {
    return Effect.runSync($cel(hidden, { form: state.value ?? {} }));
  } catch (err) {
    console.error(`[shwfed-form] failed to evaluate hidden for ${field.id}:`, err);
    return false;
  }
}
</script>

<template>
  <ClientOnly>
    <div :style="config.style">
      <FormUnitRenderer
        :unit="unit"
        :evaluate-media="evaluateMedia"
        :is-hidden="isHidden"
      />
    </div>
  </ClientOnly>
</template>
