<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';

import { Button, Image, Modal, Spin } from 'ant-design-vue';

import {
  compressImageFile,
  formatFileSize,
  formatSavingsPercent,
} from '#/utils/image-compress';

export type ImageCompressChoice = 'compressed' | 'original' | 'cancel';

export interface ImageCompressModalResult {
  choice: ImageCompressChoice;
  file: File | null;
  meta?: {
    originalSize: number;
    compressedSize?: number;
    usedCompress: boolean;
  };
}

const props = defineProps<{
  file: File;
}>();

const emit = defineEmits<{
  resolve: [result: ImageCompressModalResult];
}>();

type ModalStatus = 'compressing' | 'ready' | 'gifSkipped' | 'error';

const visible = ref(true);
const status = ref<ModalStatus>('compressing');
const progressText = ref('正在压缩，请稍候…');
const errorMessage = ref('');
const originalPreviewUrl = ref('');
const compressedPreviewUrl = ref('');
const compressedFile = ref<File | null>(null);
const originalSize = ref(0);
const compressedSize = ref(0);

const sizeText = computed(() => {
  if (!originalSize.value) return '';
  const originalText = formatFileSize(originalSize.value);
  if (status.value === 'gifSkipped') {
    return `原图 ${originalText}`;
  }
  if (compressedSize.value && status.value === 'ready') {
    const savings = formatSavingsPercent(
      originalSize.value,
      compressedSize.value,
    );
    return `原图 ${originalText} → 压缩后 ${formatFileSize(compressedSize.value)}${savings}`;
  }
  if (compressedSize.value) {
    return `原图 ${originalText} → 压缩后 ${formatFileSize(compressedSize.value)}`;
  }
  return `原图 ${originalText}`;
});

const originalButtonLabel = computed(() => {
  if (!originalSize.value) return '使用原图';
  return `使用原图 (${formatFileSize(originalSize.value)})`;
});

const compressedButtonLabel = computed(() => {
  if (!compressedSize.value) return '使用压缩图';
  return `使用压缩图 (${formatFileSize(compressedSize.value)})`;
});

function revokePreviewUrls() {
  if (originalPreviewUrl.value) {
    URL.revokeObjectURL(originalPreviewUrl.value);
    originalPreviewUrl.value = '';
  }
  if (compressedPreviewUrl.value) {
    URL.revokeObjectURL(compressedPreviewUrl.value);
    compressedPreviewUrl.value = '';
  }
}

function finish(choice: ImageCompressChoice) {
  const useCompressed = choice === 'compressed' && status.value === 'ready';
  const selectedFile =
    choice === 'cancel'
      ? null
      : useCompressed && compressedFile.value
        ? compressedFile.value
        : props.file;

  emit('resolve', {
    choice,
    file: selectedFile,
    meta:
      choice === 'cancel'
        ? undefined
        : {
            originalSize: originalSize.value,
            compressedSize: compressedSize.value || undefined,
            usedCompress: useCompressed,
          },
  });
  visible.value = false;
}

async function startCompress() {
  originalSize.value = props.file.size;
  revokePreviewUrls();
  originalPreviewUrl.value = URL.createObjectURL(props.file);

  if (props.file.type === 'image/gif') {
    status.value = 'gifSkipped';
    progressText.value = '';
    return;
  }

  status.value = 'compressing';
  progressText.value = '正在压缩，请稍候…';

  try {
    const result = await compressImageFile(props.file, {
      onProgress: (message) => {
        progressText.value = message;
      },
    });

    compressedFile.value = result.file;
    compressedSize.value = result.file.size;

    if (result.skipped) {
      status.value = 'gifSkipped';
      return;
    }

    compressedPreviewUrl.value = URL.createObjectURL(result.file);
    status.value = 'ready';
    progressText.value = '';
  } catch (error) {
    status.value = 'error';
    errorMessage.value =
      error instanceof Error ? error.message : '图片压缩失败，请使用原图或取消';
    progressText.value = '';
  }
}

onMounted(() => {
  startCompress();
});

onBeforeUnmount(() => {
  revokePreviewUrls();
});
</script>

<template>
  <Modal
    v-model:visible="visible"
    title="图片较大"
    :width="640"
    :mask-closable="false"
    :closable="false"
    :footer="null"
    @cancel="finish('cancel')"
  >
    <div class="image-compress-modal">
      <p v-if="sizeText" class="size-text">{{ sizeText }}</p>

      <div v-if="status === 'compressing'" class="compressing-panel">
        <div class="preview-single">
          <Image
            v-if="originalPreviewUrl"
            :src="originalPreviewUrl"
            alt="原图预览"
            class="preview-image"
          />
          <span v-if="originalPreviewUrl" class="preview-hint">点击预览</span>
        </div>
        <div class="compressing-status">
          <Spin />
          <span class="progress-text">{{ progressText }}</span>
        </div>
      </div>

      <div v-else-if="status === 'ready'" class="compare-panel">
        <div class="preview-column">
          <div class="preview-label">原图</div>
          <Image
            :src="originalPreviewUrl"
            alt="原图预览"
            class="preview-image"
          />
          <div class="preview-size">{{ formatFileSize(originalSize) }}</div>
          <span class="preview-hint">点击预览</span>
        </div>
        <div class="preview-column">
          <div class="preview-label">压缩后</div>
          <Image
            :src="compressedPreviewUrl"
            alt="压缩图预览"
            class="preview-image"
          />
          <div class="preview-size">{{ formatFileSize(compressedSize) }}</div>
          <span class="preview-hint">点击预览</span>
        </div>
      </div>

      <div v-else-if="status === 'gifSkipped'" class="gif-panel">
        <div class="preview-single">
          <Image
            v-if="originalPreviewUrl"
            :src="originalPreviewUrl"
            alt="原图预览"
            class="preview-image"
          />
          <span v-if="originalPreviewUrl" class="preview-hint">点击预览</span>
        </div>
        <p class="hint-text">GIF 不支持压缩，将上传原图。</p>
      </div>

      <div v-else class="error-panel">
        <div class="preview-single">
          <Image
            v-if="originalPreviewUrl"
            :src="originalPreviewUrl"
            alt="原图预览"
            class="preview-image"
          />
          <span v-if="originalPreviewUrl" class="preview-hint">点击预览</span>
        </div>
        <p class="error-text">{{ errorMessage }}</p>
      </div>

      <div class="footer-actions">
        <Button @click="finish('cancel')">取消</Button>
        <Button
          v-if="status === 'ready' || status === 'error'"
          @click="finish('original')"
        >
          {{ originalButtonLabel }}
        </Button>
        <Button
          v-if="status === 'ready'"
          type="primary"
          @click="finish('compressed')"
        >
          {{ compressedButtonLabel }}
        </Button>
        <Button
          v-if="status === 'gifSkipped'"
          type="primary"
          @click="finish('original')"
        >
          {{ originalButtonLabel }}
        </Button>
      </div>
    </div>
  </Modal>
</template>

<style lang="less" scoped>
.image-compress-modal {
  .size-text {
    margin-bottom: 16px;
    color: rgba(0, 0, 0, 0.65);
    text-align: center;
  }

  .preview-single,
  .preview-column {
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .preview-image {
    max-width: 100%;
    max-height: 220px;
    cursor: pointer;

    :deep(.ant-image-img) {
      max-width: 100%;
      max-height: 220px;
      object-fit: contain;
      border: 1px solid #f0f0f0;
      border-radius: 8px;
      background: #fafafa;
    }
  }

  .preview-label {
    margin-bottom: 8px;
    font-weight: 500;
  }

  .preview-size {
    margin-top: 8px;
    color: rgba(0, 0, 0, 0.45);
    font-size: 12px;
  }

  .preview-hint {
    margin-top: 4px;
    color: rgba(0, 0, 0, 0.35);
    font-size: 12px;
  }

  .compressing-panel,
  .gif-panel,
  .error-panel {
    display: flex;
    flex-direction: column;
    gap: 16px;
    align-items: center;
  }

  .compressing-status {
    display: flex;
    gap: 12px;
    align-items: center;
    justify-content: center;
  }

  .progress-text {
    color: rgba(0, 0, 0, 0.65);
  }

  .compare-panel {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
  }

  .hint-text,
  .error-text {
    margin: 0;
    color: rgba(0, 0, 0, 0.65);
    text-align: center;
  }

  .error-text {
    color: #ff4d4f;
  }

  .footer-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 24px;
  }
}
</style>
