<script setup lang="ts">
/**
 * Image block — upload placeholder or image preview with alt text and caption.
 * File upload is abstracted via the onUpload callback prop.
 */
import { ref, computed } from 'vue';

const props = defineProps<{
  content: Record<string, unknown>;
  onUpload?: (file: File) => Promise<{ url: string; width?: number | null; height?: number | null }>;
}>();

const emit = defineEmits<{
  update: [content: Record<string, unknown>];
}>();

const src = computed(() => (props.content.src as string) ?? '');
const alt = computed(() => (props.content.alt as string) ?? '');
const caption = computed(() => (props.content.caption as string) ?? '');
const hasImage = computed(() => !!src.value);

/** Rendered-width preset. Defaults to 'm' for new uploads; existing
 * BlockTuples without `size` render at 'l' (the viewer's fallback) to
 * preserve their pre-picker visual width. */
type ImageSize = 's' | 'm' | 'l' | 'full';
const size = computed<ImageSize>(() => {
  const v = props.content.size;
  return v === 's' || v === 'm' || v === 'l' || v === 'full' ? v : 'm';
});

const sizeOptions: Array<{ value: ImageSize; label: string; hint: string }> = [
  { value: 's', label: 'S', hint: 'small (~320px)' },
  { value: 'm', label: 'M', hint: 'medium (~540px)' },
  { value: 'l', label: 'L', hint: 'large (~760px)' },
  { value: 'full', label: 'Full', hint: 'full width' },
];

function updateField(field: string, value: string): void {
  emit('update', { ...props.content, [field]: value });
}

function setSize(s: ImageSize): void {
  emit('update', { ...props.content, size: s });
}

const uploading = ref(false);
const uploadError = ref('');

async function handleFileSelect(event: Event): Promise<void> {
  const input = event.target as HTMLInputElement;
  const file = input.files?.[0];
  if (!file) return;
  if (!props.onUpload) {
    uploadError.value = 'No upload handler provided';
    return;
  }

  uploading.value = true;
  uploadError.value = '';

  try {
    const result = await props.onUpload(file);

    emit('update', {
      src: result.url,
      alt: alt.value || file.name.replace(/\.[^.]+$/, ''),
      caption: caption.value,
    });
  } catch (err: unknown) {
    uploadError.value = (err as { data?: { statusMessage?: string } })?.data?.statusMessage ?? 'Upload failed';
  } finally {
    uploading.value = false;
  }
}
</script>

<template>
  <div class="cpub-image-block">
    <!-- No image — upload placeholder -->
    <template v-if="!hasImage">
      <label class="cpub-image-placeholder" :class="{ 'cpub-image-uploading': uploading }">
        <input type="file" accept="image/*" class="cpub-sr-only" :disabled="uploading" @change="handleFileSelect" />
        <template v-if="uploading">
          <i class="fa-solid fa-circle-notch fa-spin cpub-image-placeholder-icon"></i>
          <span class="cpub-image-placeholder-text">Uploading...</span>
        </template>
        <template v-else>
          <i class="fa-regular fa-image cpub-image-placeholder-icon"></i>
          <span class="cpub-image-placeholder-text">Click to upload image or drag and drop</span>
          <span class="cpub-image-placeholder-hint">JPG, PNG, WebP, GIF · 10 MB max</span>
        </template>
      </label>
      <div v-if="uploadError" class="cpub-image-error">{{ uploadError }}</div>
      <div class="cpub-image-url-row">
        <input
          class="cpub-image-url-input"
          type="url"
          placeholder="Or paste image URL..."
          :value="src"
          @input="updateField('src', ($event.target as HTMLInputElement).value)"
        />
      </div>
    </template>

    <!-- Has image — show preview -->
    <template v-else>
      <div class="cpub-image-preview">
        <img :src="src" :alt="alt" class="cpub-image-preview-img" />
        <button class="cpub-image-remove" title="Remove image" @click="updateField('src', '')">
          <i class="fa-solid fa-xmark"></i>
        </button>
      </div>
      <div class="cpub-image-fields">
        <input
          class="cpub-image-field"
          type="text"
          :value="alt"
          placeholder="Alt text (describes the image)..."
          aria-label="Image alt text"
          @input="updateField('alt', ($event.target as HTMLInputElement).value)"
        />
        <input
          class="cpub-image-field"
          type="text"
          :value="caption"
          placeholder="Caption (optional)..."
          aria-label="Image caption"
          @input="updateField('caption', ($event.target as HTMLInputElement).value)"
        />
      </div>
      <div class="cpub-image-size-row" role="group" aria-label="Image size">
        <span class="cpub-image-size-label">Size</span>
        <div class="cpub-image-size-options">
          <button
            v-for="opt in sizeOptions"
            :key="opt.value"
            type="button"
            class="cpub-image-size-btn"
            :class="{ 'cpub-image-size-btn-active': size === opt.value }"
            :title="opt.hint"
            :aria-pressed="size === opt.value"
            @click="setSize(opt.value)"
          >
            {{ opt.label }}
          </button>
        </div>
      </div>
    </template>
  </div>
</template>

<style scoped>
.cpub-image-block {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.cpub-image-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 36px 24px;
  border: 2px dashed var(--border2);
  background: var(--surface2);
  cursor: pointer;
  transition: all 0.12s;
}

.cpub-image-placeholder:hover {
  border-color: var(--accent);
  background: var(--accent-bg);
}

.cpub-image-placeholder-icon {
  font-size: 28px;
  color: var(--text-faint);
}

.cpub-image-placeholder:hover .cpub-image-placeholder-icon {
  color: var(--accent);
}

.cpub-image-placeholder-text {
  font-size: 12px;
  color: var(--text-dim);
}

.cpub-image-placeholder-hint {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-faint);
}

.cpub-image-uploading {
  pointer-events: none;
  opacity: 0.7;
}

.cpub-image-error {
  padding: 6px 10px;
  font-size: 11px;
  color: var(--red);
  background: var(--red-bg);
  border: var(--border-width-default) solid var(--red-border);
}

.cpub-image-url-row {
  padding: 8px 0 0;
}

.cpub-image-url-input {
  width: 100%;
  padding: 6px 10px;
  font-size: 12px;
  background: var(--surface);
  border: var(--border-width-default) solid var(--border);
  color: var(--text);
  outline: none;
}

.cpub-image-url-input:focus {
  border-color: var(--accent);
}

.cpub-image-url-input::placeholder {
  color: var(--text-faint);
}

.cpub-image-preview {
  position: relative;
}

.cpub-image-preview-img {
  width: 100%;
  max-height: 500px;
  object-fit: contain;
  display: block;
  background: var(--surface2);
  border: var(--border-width-default) solid var(--border);
}

.cpub-image-remove {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 28px;
  height: 28px;
  background: var(--text);
  border: none;
  color: var(--surface);
  font-size: 12px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.12s;
}

.cpub-image-preview:hover .cpub-image-remove {
  opacity: 1;
}

.cpub-image-remove:hover {
  background: var(--red);
}

.cpub-image-fields {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: 6px;
}

.cpub-image-field {
  width: 100%;
  padding: 5px 8px;
  font-size: 11px;
  background: var(--surface);
  border: var(--border-width-default) solid var(--border2);
  color: var(--text-dim);
  outline: none;
  font-style: italic;
}

.cpub-image-field:focus {
  border-color: var(--accent);
  color: var(--text);
  font-style: normal;
}

.cpub-image-field::placeholder {
  color: var(--text-faint);
  font-style: italic;
}

.cpub-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

.cpub-image-size-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 6px;
}

.cpub-image-size-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.cpub-image-size-options {
  display: inline-flex;
  border: var(--border-width-default) solid var(--border2);
  background: var(--surface);
}

.cpub-image-size-btn {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: transparent;
  border: none;
  color: var(--text-dim);
  padding: 4px 10px;
  cursor: pointer;
  border-right: var(--border-width-default) solid var(--border2);
  transition: background 0.12s, color 0.12s;
}

.cpub-image-size-btn:last-child {
  border-right: none;
}

.cpub-image-size-btn:hover {
  background: var(--surface2);
  color: var(--text);
}

.cpub-image-size-btn-active {
  background: var(--accent);
  color: var(--color-on-accent, var(--surface));
}

.cpub-image-size-btn-active:hover {
  background: var(--accent);
  color: var(--color-on-accent, var(--surface));
}
</style>
