<script>
import theme from "#build/b24ui/file-upload";
</script>

<script setup>
import { computed, toRef, watch } from "vue";
import { Primitive, VisuallyHidden } from "reka-ui";
import { createReusableTemplate } from "@vueuse/core";
import { useAppConfig, useLocale } from "#imports";
import { useFormField } from "../composables/useFormField";
import { useFileUpload } from "../composables/useFileUpload";
import { tv } from "../utils/tv";
import icons from "../dictionary/icons";
import B24Avatar from "./Avatar.vue";
import B24Button from "./Button.vue";
defineOptions({ inheritAttrs: false });
const props = defineProps({
  as: { type: null, required: false },
  id: { type: String, required: false },
  name: { type: String, required: false },
  icon: { type: [Function, Object], required: false },
  label: { type: String, required: false },
  description: { type: String, required: false },
  color: { type: null, required: false },
  variant: { type: null, required: false },
  size: { type: null, required: false },
  layout: { type: null, required: false, default: "grid" },
  position: { type: null, required: false, default: "outside" },
  highlight: { type: Boolean, required: false },
  accept: { type: String, required: false, default: "*" },
  multiple: { type: Boolean, required: false, default: false },
  reset: { type: Boolean, required: false, default: false },
  dropzone: { type: Boolean, required: false, default: true },
  interactive: { type: Boolean, required: false, default: true },
  required: { type: Boolean, required: false },
  disabled: { type: Boolean, required: false },
  fileIcon: { type: [Function, Object], required: false },
  fileDelete: { type: [Boolean, Object], required: false, default: true },
  fileDeleteIcon: { type: [Function, Object], required: false },
  preview: { type: Boolean, required: false, default: true },
  class: { type: null, required: false },
  b24ui: { type: null, required: false }
});
const emits = defineEmits(["change"]);
const slots = defineSlots();
const modelValue = defineModel({ type: null });
const appConfig = useAppConfig();
const { t } = useLocale();
const [DefineFilesTemplate, ReuseFilesTemplate] = createReusableTemplate();
const { isDragging, open, inputRef, dropzoneRef } = useFileUpload({
  accept: props.accept,
  reset: props.reset,
  multiple: props.multiple,
  dropzone: props.dropzone,
  onUpdate
});
const { emitFormInput, emitFormChange, id, name, disabled, ariaAttrs } = useFormField(props);
const variant = computed(() => props.multiple ? "area" : props.variant);
const layout = computed(() => props.variant === "button" && !props.multiple ? "grid" : props.layout);
const position = computed(() => {
  if (layout.value === "grid" && props.multiple) {
    return "inside";
  }
  if (variant.value === "button") {
    return "outside";
  }
  return props.position;
});
const b24ui = computed(() => tv({ extend: tv(theme), ...appConfig.b24ui?.fileUpload || {} })({
  dropzone: props.dropzone,
  interactive: props.interactive,
  color: props.color,
  size: props.size,
  variant: variant.value,
  layout: layout.value,
  position: position.value,
  multiple: props.multiple,
  highlight: props.highlight,
  disabled: props.disabled
}));
function createObjectUrl(file) {
  return URL.createObjectURL(file);
}
function formatFileSize(bytes) {
  if (bytes === 0) {
    return "0B";
  }
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  const size = bytes / Math.pow(k, i);
  const formattedSize = i === 0 ? size.toString() : size.toFixed(0);
  return `${formattedSize}${sizes[i]}`;
}
function onUpdate(files, reset = false) {
  if (props.multiple) {
    if (reset) {
      modelValue.value = files;
    } else {
      const existingFiles = modelValue.value || [];
      modelValue.value = [...existingFiles, ...files || []];
    }
  } else {
    modelValue.value = files?.[0];
  }
  const event = new Event("change", { target: { value: modelValue.value } });
  emits("change", event);
  emitFormChange();
  emitFormInput();
}
function removeFile(index) {
  if (!modelValue.value) {
    return;
  }
  if (!props.multiple || index === void 0) {
    onUpdate([], true);
    dropzoneRef.value?.focus();
    return;
  }
  const files = [...modelValue.value];
  files.splice(index, 1);
  onUpdate(files, true);
  dropzoneRef.value?.focus();
}
watch(modelValue, (newValue) => {
  const hasModelReset = props.multiple ? !newValue?.length : !newValue;
  if (hasModelReset && inputRef.value) {
    inputRef.value.$el.value = "";
  }
});
defineExpose({
  inputRef: toRef(() => inputRef.value?.$el),
  dropzoneRef
});
</script>

<template>
  <DefineFilesTemplate>
    <template v-if="props.preview && modelValue && (Array.isArray(modelValue) ? modelValue.length : true)">
      <slot name="files-top" :files="modelValue" :open="open" :remove-file="removeFile" />

      <div data-slot="files" :class="b24ui.files({ class: props.b24ui?.files })">
        <slot name="files" :files="modelValue">
          <div
            v-for="(file, index) in Array.isArray(modelValue) ? modelValue : [modelValue]"
            :key="file.name"
            data-slot="file"
            :class="b24ui.file({ class: props.b24ui?.file })"
          >
            <slot name="file" :file="file" :index="index">
              <slot name="file-leading" :file="file" :index="index" :b24ui="b24ui">
                <B24Avatar
                  :as="{ img: 'img' }"
                  :src="createObjectUrl(file)"
                  :icon="fileIcon || icons.file"
                  :size="props.size"
                  data-slot="fileLeadingAvatar"
                  :class="b24ui.fileLeadingAvatar({ class: props.b24ui?.fileLeadingAvatar })"
                />
              </slot>

              <div data-slot="fileWrapper" :class="b24ui.fileWrapper({ class: props.b24ui?.fileWrapper })">
                <span data-slot="fileName" :class="b24ui.fileName({ class: props.b24ui?.fileName })">
                  <slot name="file-name" :file="file" :index="index">
                    {{ file.name }}
                  </slot>
                </span>

                <span data-slot="fileSize" :class="b24ui.fileSize({ class: props.b24ui?.fileSize })">
                  <slot name="file-size" :file="file" :index="index">
                    {{ formatFileSize(file.size) }}
                  </slot>
                </span>
              </div>

              <slot name="file-trailing" :file="file" :index="index" :b24ui="b24ui">
                <B24Button
                  v-if="fileDelete"
                  v-bind="{
  ...layout === 'grid' ? {
    color: 'air-secondary-no-accent',
    size: 'xs'
  } : {
    color: 'air-tertiary-no-accent',
    size: 'xs'
  },
  ...typeof fileDelete === 'object' ? fileDelete : void 0
}"
                  :aria-label="t('fileUpload.removeFile', { filename: file.name })"
                  :icon="fileDeleteIcon || icons.close"
                  data-slot="fileTrailingButton"
                  :class="b24ui.fileTrailingButton({ class: props.b24ui?.fileTrailingButton })"
                  @click.stop.prevent="removeFile(index)"
                />
              </slot>
            </slot>
          </div>
        </slot>
      </div>

      <slot name="files-bottom" :files="modelValue" :open="open" :remove-file="removeFile" />
    </template>
  </DefineFilesTemplate>

  <Primitive :as="as" data-slot="root" :class="b24ui.root({ class: [props.b24ui?.root, props.class] })">
    <slot :open="open" :remove-file="removeFile" :b24ui="b24ui">
      <component
        :is="variant === 'button' ? 'button' : 'div'"
        ref="dropzoneRef"
        :type="variant === 'button' ? 'button' : void 0"
        :role="variant === 'button' ? void 0 : 'button'"
        :data-dragging="isDragging"
        data-slot="base"
        :class="b24ui.base({ class: props.b24ui?.base })"
        :tabindex="interactive && !disabled ? 0 : -1"
        @click="interactive && !disabled && open()"
        @keydown.space.prevent
        @keyup.enter.space="interactive && !disabled && open()"
      >
        <ReuseFilesTemplate v-if="position === 'inside'" />

        <div
          v-if="position === 'inside' ? multiple ? !modelValue?.length : !modelValue : true"
          data-slot="wrapper"
          :class="b24ui.wrapper({ class: props.b24ui?.wrapper })"
        >
          <slot name="leading" :b24ui="b24ui">
            <Component
              :is="icon || icons.upload"
              v-if="variant === 'button'"
              data-slot="icon"
              :class="b24ui.icon({ class: props.b24ui?.icon })"
            />
            <B24Avatar
              v-else
              :icon="icon || icons.upload"
              :size="props.size"
              data-slot="avatar"
              :class="b24ui.avatar({ class: props.b24ui?.avatar })"
            />
          </slot>

          <template v-if="variant !== 'button'">
            <div v-if="label || !!slots.label" data-slot="label" :class="b24ui.label({ class: props.b24ui?.label })">
              <slot name="label">
                {{ label }}
              </slot>
            </div>
            <div v-if="description || !!slots.description" data-slot="description" :class="b24ui.description({ class: props.b24ui?.description })">
              <slot name="description">
                {{ description }}
              </slot>
            </div>

            <div v-if="!!slots.actions" data-slot="actions" :class="b24ui.actions({ class: props.b24ui?.actions })">
              <slot name="actions" :files="modelValue" :open="open" :remove-file="removeFile" />
            </div>
          </template>
        </div>
      </component>

      <ReuseFilesTemplate v-if="position === 'outside'" />
    </slot>

    <VisuallyHidden
      :id="id"
      ref="inputRef"
      as="input"
      type="file"
      feature="fully-hidden"
      :name="name"
      :accept="accept"
      :multiple="multiple"
      :required="required"
      :disabled="disabled"
      v-bind="{ ...$attrs, ...ariaAttrs }"
    />
  </Primitive>
</template>
