<script setup lang="ts">
import { UiButton } from '@/components/UiButton';
import { UiFileType } from '@/components/UiFileType';
import { UiIcon } from '@/components/UiIcon';
import { UiProgress } from '@/components/UiProgress';
import { cn } from '@/lib/utils';
import { computed } from 'vue';
import type { UiFileItemProps } from './types';

defineOptions({
  name: 'UiFileItem',
});

/** @see {@link UiFileItemProps} */
const props = withDefaults(defineProps<UiFileItemProps>(), {
  typeLabel: undefined,
  sizeLabel: undefined,
  progress: null,
  error: null,
  removeLabel: 'Remove file',
  progressLabel: undefined,
  removable: true,
  disabled: false,
  size: 'default',
  // `mediaTone` and `mediaIcon` are intentionally undefined by default so
  // `UiFileType`'s extension-based auto-detection can decide both. Pass
  // them explicitly to override.
  mediaTone: undefined,
  mediaIcon: undefined,
});

/* eslint-disable jsdoc/require-jsdoc -- emit payload is empty tuple */
const emit = defineEmits<{
  remove: [];
}>();
/* eslint-enable jsdoc/require-jsdoc */

/**
 * Drops the last `.ext` segment; leaves `.gitignore`-style names unchanged.
 * @param name - Full file name
 */
function filenameWithoutExtension(name: string): string {
  const i = name.lastIndexOf('.');
  if (i <= 0) return name;
  return name.slice(0, i);
}

const displayFilename = computed(() =>
  filenameWithoutExtension(props.filename),
);

const isError = computed(() => props.error != null && props.error !== '');

const isWithProgress = computed(
  () =>
    !isError.value &&
    props.progress != null &&
    props.progress >= 0 &&
    props.progress < 100,
);

const showMeta = computed(
  () =>
    !isError.value &&
    !isWithProgress.value &&
    (props.typeLabel || props.sizeLabel),
);

const isSmall = computed(() => props.size === 'small');

const containerClass = computed(() =>
  cn(
    'relative flex w-full max-w-full min-w-0 items-center overflow-hidden rounded-xl',
    'bg-background-surface-muted-subtle',
    isSmall.value ? 'gap-3 p-2' : 'gap-3 p-3',
    props.disabled && 'opacity-50',
  ),
);

// Body column: title plus one of {progress, error, meta}. The vertical gap
// shrinks for compact rows (small size or error/multi states).
const contentClass = computed(() =>
  cn(
    'flex min-w-0 flex-1 flex-col items-start',
    isSmall.value || isError.value ? 'gap-0.5' : 'gap-1.5',
  ),
);

const titleClass = computed(() =>
  cn(
    'block w-full min-w-0 truncate text-content-on-surface-primary',
    isSmall.value ? 'label-12-medium' : 'body-14-medium',
  ),
);

const progressClass = computed(() =>
  cn('w-full', isSmall.value ? 'h-1' : 'h-1.5'),
);
</script>

<template>
  <div
    data-slot="file-item"
    :class="containerClass"
    :inert="disabled ? true : undefined"
  >
    <UiFileType
      :filename="filename"
      :tone="mediaTone"
      :icon="mediaIcon"
      :error="isError"
      :loading="isWithProgress"
    />

    <div data-slot="file-item-content" :class="contentClass">
      <span :class="titleClass" :aria-label="filename">
        {{ displayFilename }}
      </span>

      <UiProgress
        v-if="isWithProgress"
        :model-value="progress!"
        :class="progressClass"
        :aria-label="progressLabel ?? filename"
      />

      <p
        v-else-if="isError"
        class="label-12-medium text-content-on-status-error-inverse w-full truncate"
        role="alert"
      >
        {{ error }}
      </p>

      <div
        v-else-if="showMeta"
        data-slot="file-item-meta"
        class="label-12-medium text-content-on-surface-muted flex w-full min-w-0 items-center gap-2"
      >
        <span v-if="typeLabel" class="truncate">{{ typeLabel }}</span>
        <span
          v-if="typeLabel && sizeLabel"
          aria-hidden="true"
          class="text-content-on-surface-muted shrink-0"
          >|</span
        >
        <span v-if="sizeLabel" class="truncate">{{ sizeLabel }}</span>
      </div>
    </div>

    <UiButton
      v-if="removable"
      type="button"
      variant="ghost"
      size="icon-sm"
      class="shrink-0"
      :disabled="disabled"
      :aria-label="removeLabel"
      @click="emit('remove')"
    >
      <UiIcon name="x" :size="16" aria-hidden="true" />
    </UiButton>
  </div>
</template>
