<script setup>
import { Icon } from "@iconify/vue";
import { Effect } from "effect";
import { computed, nextTick } from "vue";
import { useI18n } from "vue-i18n";
import { cel as _rawCel } from "../../../../../utils/cel";
import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
import { getLocalizedText } from "../../../../../share/locale";
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupNumberField } from "../../../../ui/input-group";
import { useFieldValue } from "../../../../form/utils/field-value";
defineOptions({ name: "ShwfedTableNumberInputRendererRuntime" });
const props = defineProps({
  column: { type: null, required: true },
  ctx: { type: Object, required: true }
});
const { locale } = useI18n();
const inherited = injectCELContext();
const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
const placeholderText = computed(
  () => props.column.placeholder ? getLocalizedText(props.column.placeholder, locale.value) : void 0
);
function evalBool(expression, label) {
  if (!expression) return false;
  try {
    return Effect.runSync($cel(expression)) === true;
  } catch (e) {
    console.error(`[shwfed-table] number-input ${label} failed`, e);
    return false;
  }
}
function evalNumber(expression, label) {
  if (!expression) return void 0;
  try {
    const result = Effect.runSync($cel(expression));
    return typeof result === "number" && Number.isFinite(result) ? result : void 0;
  } catch (e) {
    console.error(`[shwfed-table] number-input ${label} failed`, e);
    return void 0;
  }
}
const isHidden = computed(() => evalBool(props.column.hidden, "hidden"));
const isDisabled = computed(() => evalBool(props.column.disabled, "disabled"));
const isReadonly = computed(() => evalBool(props.column.readonly, "readonly"));
const effectiveReadonly = computed(
  () => isReadonly.value || props.column.derived?.mode === "formula"
);
const minValue = computed(() => evalNumber(props.column.min, "min"));
const maxValue = computed(() => evalNumber(props.column.max, "max"));
const formatOptions = computed(() => ({
  useGrouping: false,
  maximumFractionDigits: 20
}));
const { draft, commit } = useFieldValue({
  binding: () => props.column.binding,
  fromState: (raw) => {
    if (typeof raw === "number" && Number.isFinite(raw)) return raw;
    if (typeof raw === "string" && raw.length > 0) {
      const n = Number(raw);
      return Number.isFinite(n) ? n : void 0;
    }
    return void 0;
  },
  toState: (next) => {
    if (typeof next !== "number" || !Number.isFinite(next)) return null;
    return props.column.valueAsString ? String(next) : next;
  }
});
function applyRounding(n, precision, mode) {
  const factor = 10 ** precision;
  switch (mode) {
    case "floor":
      return Math.floor(n * factor) / factor;
    case "ceil":
      return Math.ceil(n * factor) / factor;
    default:
      return Math.round(n * factor) / factor;
  }
}
const showClear = computed(() => !isDisabled.value && draft.value !== void 0);
function handleClear() {
  draft.value = void 0;
  commit();
}
async function onBlur() {
  await nextTick();
  const precision = props.column.precision;
  if (precision !== void 0) {
    const current = draft.value;
    if (current !== void 0) {
      const rounded = applyRounding(current, precision, props.column.roundingMode ?? "round");
      if (rounded !== current) draft.value = rounded;
    }
  }
  commit();
}
</script>

<template>
  <!--
    Row budget matches the text-input cell: 2px outer inset around an `h-7`
    (28px) input — 32px total. Every branch (hidden / readonly / editable)
    is sized identically so rows do not jump when a CEL condition flips.
    Both `text-[0.75rem]` breakpoints are explicit so `Input`'s default
    `md:text-sm` cannot beat the override at md+.
  -->
  <div class="p-[0.125rem] w-full">
    <span
      v-if="isHidden"
      class="block h-7 w-full"
    />
    <span
      v-else-if="effectiveReadonly"
      class="flex items-center h-7 w-full px-2 text-[0.75rem] text-zinc-700 truncate"
    >
      {{ draft ?? "\u2014" }}
    </span>
    <InputGroup
      v-else
      class="group/number-input h-7 rounded border-zinc-200/30 hover:border-zinc-200 focus-within:border-zinc-200 transition-colors ease-out duration-180"
      :data-disabled="isDisabled ? 'true' : void 0"
    >
      <InputGroupNumberField
        :model-value="draft"
        :step="column.step"
        :step-snapping="column.step !== void 0"
        :min="minValue"
        :max="maxValue"
        :format-options="formatOptions"
        :disabled="isDisabled"
        :placeholder="placeholderText"
        input-class="h-7 text-[0.75rem] md:text-[0.75rem] px-2 text-left"
        @update:model-value="(v) => draft = v"
        @blur="onBlur"
      />
      <InputGroupAddon
        v-if="showClear"
        align="inline-end"
        class="[@media(hover:hover)]:opacity-0 transition-opacity group-hover/number-input:opacity-100 focus-within:opacity-100"
      >
        <InputGroupButton
          size="icon-xs"
          data-slot="number-input-clear"
          class="size-4 text-zinc-500 hover:text-zinc-700"
          tabindex="-1"
          @mousedown.prevent
          @click.stop="handleClear"
        >
          <Icon
            icon="fluent:dismiss-20-regular"
            class="size-3"
          />
        </InputGroupButton>
      </InputGroupAddon>
    </InputGroup>
  </div>
</template>
