<script setup lang="ts">
import { ref, computed, watch, nextTick, onBeforeUnmount } from 'vue';
import { Handle, Position } from '@vue-flow/core';
import HandleComponents from './HandleComponents.vue';
import NodeTitle from './NodeTitle.vue';
import { useNodeUpload } from '../composables/useNodeUpload';
import {
  cropSignal,
  cropActive,
  cropConfirmSignal,
  cropCancelSignal,
  cropProportionValue,
  proportionOptions,
} from '../composables/useCropSignal';
import {
  doodleSignal,
  doodleActive,
  doodleConfirmSignal,
  doodleCancelSignal,
  doodleUndoSignal,
  doodleClearSignal,
  doodlePenColor,
  doodlePenSize,
} from '../composables/useDoodleSignal';
import Cropper from 'v3-cropper';

// ============ Props & Emits ============
const props = defineProps(['id', 'data', 'position', 'selected']);

const emit = defineEmits<{
  'node-added': [nodeId: string];
  'upload-success': [];
  'crop-confirm': [img: string];
  'doodle-confirm': [img: string];
}>();

// ============ 节点基础状态 ============
const imageUrl = ref<string>(props.data?.url ?? '');
const hasImage = computed(() => !!imageUrl.value);
const isChild = computed(() => !!props.data?.isChildOf);

watch(
  () => props.data?.url,
  (url) => {
    if (url && url !== imageUrl.value) {
      imageUrl.value = url;
    }
  }
);

// 加载图片获取自然尺寸（裁剪/涂鸦量尺寸用，无需 crossOrigin）
const loadImageSize = (url: string): Promise<{ width: number; height: number }> => {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
    img.onerror = () => resolve({ width: 0, height: 0 });
    img.src = url;
  });
};

// ============ 上传功能 ============
const { uploading, triggerUpload } = useNodeUpload(props.id, 'image', {
  onSuccess: () => emit('upload-success'),
});

// ============ 裁剪模式状态 ============
const cropMode = ref(false);
const cropImageUrl = ref(''); // 裁剪专用图片 URL（base64），不污染节点本身的 imageUrl
const croppedResult = ref(''); // Cropper 实时回调的裁剪结果
const cropperKey = ref(0); // 切比例时 ++ 强制重挂载 Cropper
const imageNaturalSize = ref({ width: 0, height: 0 }); // 原图自然尺寸（用于原图比例）
const cropDisplayWidth = ref(600); // 裁剪区域临时放大宽度
const cropDisplayHeight = ref(600); // 裁剪区域临时放大高度

// ============ 裁剪比例配置 ============
// proportionOptions 已移至 useCropSignal 共享，vueFlow 工具条与本地 Cropper 共用

const proportionValue = cropProportionValue; // 共享 ref，vueFlow 工具条 select 双向绑定
const isFreeMode = computed(() => proportionValue.value === 'free');
const currentRatio = computed(() => {
  const opt = proportionOptions.find((o) => o.value === proportionValue.value);
  if (!opt) return 0;
  if (proportionValue.value === 'original') {
    const { width: w, height: h } = imageNaturalSize.value;
    return w && h ? w / h : 0;
  }
  return opt.ratio;
});

const cutWidth = ref(0);
const cutHeight = ref(0);

// ============ 裁剪工具函数 ============
// 跨域图片转 base64 data URL，避免 v3-cropper canvas.toDataURL 报 Tainted canvases
const urlToDataUrl = (url: string): Promise<string> => {
  return new Promise((resolve, reject) => {
    if (url.startsWith('data:') || url.startsWith('/')) {
      resolve(url);
      return;
    }
    fetch(url, { mode: 'cors' })
      .then((res) => res.blob())
      .then((blob) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result as string);
        reader.onerror = reject;
        reader.readAsDataURL(blob);
      })
      .catch(reject);
  });
};

// 按当前比例在裁剪区域内最大化裁剪框（留 20% 边距）
const recalcCutSize = () => {
  const cw = cropDisplayWidth.value;
  const ch = cropDisplayHeight.value;
  const ratio = currentRatio.value;

  if (!ratio) {
    cutWidth.value = Math.floor(cw * 0.6);
    cutHeight.value = Math.floor(ch * 0.6);
    return;
  }

  if (cw / ch > ratio) {
    cutHeight.value = Math.floor(ch * 0.8);
    cutWidth.value = Math.floor(cutHeight.value * ratio);
  } else {
    cutWidth.value = Math.floor(cw * 0.8);
    cutHeight.value = Math.floor(cutWidth.value / ratio);
  }
};

// ============ 裁剪操作 ============
// 进入裁剪模式：转 base64 → 量尺寸 → 挂载 Cropper
const enterCrop = async () => {
  if (!hasImage.value) return;

  let dataUrl: string;
  try {
    dataUrl = await urlToDataUrl(imageUrl.value);
  } catch {
    dataUrl = imageUrl.value;
  }
  cropImageUrl.value = dataUrl;

  const size = await loadImageSize(dataUrl);
  imageNaturalSize.value = size;
  const maxDim = 600;
  if (size.width >= size.height) {
    cropDisplayWidth.value = maxDim;
    cropDisplayHeight.value = Math.floor((size.height / size.width) * maxDim);
  } else {
    cropDisplayHeight.value = maxDim;
    cropDisplayWidth.value = Math.floor((size.width / size.height) * maxDim);
  }
  recalcCutSize();

  cropMode.value = true;
  cropActive.value = true;
  croppedResult.value = '';
  cropperKey.value++;
  // 重置比例为自由比例
  cropProportionValue.value = 'free';
};

// 确认裁剪：emit 结果给 vueFlow 创建子节点
const confirmCrop = () => {
  if (!croppedResult.value) {
    cropMode.value = false;
    cropActive.value = false;
    return;
  }
  emit('crop-confirm', croppedResult.value);
  cropMode.value = false;
  cropActive.value = false;
};

// 取消裁剪
const cancelCrop = () => {
  cropMode.value = false;
  cropActive.value = false;
  croppedResult.value = '';
};

// 切换比例：由 vueFlow 工具条修改共享 cropProportionValue 触发，重算裁剪框 + 重挂载 Cropper
watch(cropProportionValue, () => {
  if (!cropMode.value) return;
  recalcCutSize();
  cropperKey.value++;
});

// Cropper 实时回调：保存裁剪结果
const onGetImg = (img: string) => {
  croppedResult.value = img;
};

// ============ 裁剪信号监听 ============
// 监听 vueFlow 上方面板的裁剪信号，仅选中节点响应
watch(cropSignal, () => {
  if (props.selected && hasImage.value && !cropMode.value) {
    enterCrop();
  }
});

// 失去选中时：若处于裁剪/涂鸦模式则自动取消，恢复节点大小并清除 active 状态
watch(
  () => props.selected,
  (selected) => {
    if (selected) return;
    if (cropMode.value) cancelCrop();
    if (doodleMode.value) exitDoodle();
  }
);

// 监听 vueFlow 工具条的确认信号
watch(cropConfirmSignal, () => {
  if (cropMode.value) confirmCrop();
});

// 监听 vueFlow 工具条的取消信号
watch(cropCancelSignal, () => {
  if (cropMode.value) cancelCrop();
});

// ============ 涂鸦模式 ============
const doodleMode = ref(false);
const doodleCanvasRef = ref<HTMLCanvasElement | null>(null);
const doodleImageUrl = ref(''); // 涂鸦背景图 base64
const doodleDisplayWidth = ref(600);
const doodleDisplayHeight = ref(600);
let doodleCtx: CanvasRenderingContext2D | null = null;
let doodleDrawing = false;
let doodleLastX = 0;
let doodleLastY = 0;
let doodleImageLoaded = false;
const doodleHistory: string[] = [];
const DOODLE_MAX_HISTORY = 30;

// 跨域图片转 base64（复用裁剪的同款工具）
const doodleUrlToDataUrl = (url: string): Promise<string> => {
  return new Promise((resolve, reject) => {
    if (url.startsWith('data:') || url.startsWith('/')) {
      resolve(url);
      return;
    }
    fetch(url, { mode: 'cors' })
      .then((res) => res.blob())
      .then((blob) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result as string);
        reader.onerror = reject;
        reader.readAsDataURL(blob);
      })
      .catch(reject);
  });
};

// 进入涂鸦模式
const enterDoodle = async () => {
  if (!hasImage.value) return;
  let dataUrl: string;
  try {
    dataUrl = await doodleUrlToDataUrl(imageUrl.value);
  } catch {
    dataUrl = imageUrl.value;
  }
  doodleImageUrl.value = dataUrl;

  const size = await loadImageSize(dataUrl);
  const maxDim = 600;
  if (size.width >= size.height) {
    doodleDisplayWidth.value = maxDim;
    doodleDisplayHeight.value = Math.floor((size.height / size.width) * maxDim);
  } else {
    doodleDisplayHeight.value = maxDim;
    doodleDisplayWidth.value = Math.floor((size.width / size.height) * maxDim);
  }

  doodleMode.value = true;
  doodleActive.value = true;

  // 等 canvas 挂载后初始化
  await nextTick();
  initDoodleCanvas();
};

// 初始化 canvas：绘制背景图 + 绑定事件 + 保存初始历史
const initDoodleCanvas = () => {
  const canvas = doodleCanvasRef.value;
  if (!canvas) return;
  canvas.width = doodleDisplayWidth.value;
  canvas.height = doodleDisplayHeight.value;
  doodleCtx = canvas.getContext('2d');

  const img = new Image();
  img.crossOrigin = 'anonymous';
  img.onload = () => {
    if (!doodleCtx) return;
    doodleCtx.drawImage(img, 0, 0, canvas.width, canvas.height);
    doodleImageLoaded = true;
    saveDoodleHistory();
  };
  img.src = doodleImageUrl.value;

  // 鼠标事件（stopPropagation 防止触发节点拖动）
  canvas.addEventListener('mousedown', onDoodleMouseDown);
  canvas.addEventListener('mousemove', onDoodleMouseMove);
  canvas.addEventListener('mouseup', onDoodleMouseUp);
  canvas.addEventListener('mouseleave', onDoodleMouseUp);
  // 触摸事件
  canvas.addEventListener('touchstart', onDoodleTouchStart, { passive: false });
  canvas.addEventListener('touchmove', onDoodleTouchMove, { passive: false });
  canvas.addEventListener('touchend', onDoodleTouchEnd, { passive: false });
};

const getDoodlePos = (clientX: number, clientY: number) => {
  const canvas = doodleCanvasRef.value!;
  const rect = canvas.getBoundingClientRect();
  return {
    x: (clientX - rect.left) * (canvas.width / rect.width),
    y: (clientY - rect.top) * (canvas.height / rect.height),
  };
};

const onDoodleMouseDown = (e: MouseEvent) => {
  if (!doodleImageLoaded) return;
  e.stopPropagation();
  doodleDrawing = true;
  const { x, y } = getDoodlePos(e.clientX, e.clientY);
  doodleLastX = x;
  doodleLastY = y;
};

const onDoodleMouseMove = (e: MouseEvent) => {
  if (!doodleDrawing || !doodleImageLoaded || !doodleCtx) return;
  e.stopPropagation();
  const { x, y } = getDoodlePos(e.clientX, e.clientY);
  doodleCtx.beginPath();
  doodleCtx.moveTo(doodleLastX, doodleLastY);
  doodleCtx.lineTo(x, y);
  doodleCtx.strokeStyle = doodlePenColor.value;
  doodleCtx.lineWidth = doodlePenSize.value;
  doodleCtx.lineCap = 'round';
  doodleCtx.lineJoin = 'round';
  doodleCtx.stroke();
  doodleLastX = x;
  doodleLastY = y;
};

const onDoodleMouseUp = () => {
  if (doodleDrawing) {
    doodleDrawing = false;
    saveDoodleHistory();
  }
};

const onDoodleTouchStart = (e: TouchEvent) => {
  e.preventDefault();
  const t = e.touches[0];
  onDoodleMouseDown(new MouseEvent('mousedown', { clientX: t.clientX, clientY: t.clientY }));
};

const onDoodleTouchMove = (e: TouchEvent) => {
  e.preventDefault();
  const t = e.touches[0];
  onDoodleMouseMove(new MouseEvent('mousemove', { clientX: t.clientX, clientY: t.clientY }));
};

const onDoodleTouchEnd = (e: TouchEvent) => {
  e.preventDefault();
  onDoodleMouseUp();
};

const saveDoodleHistory = () => {
  const canvas = doodleCanvasRef.value;
  if (!canvas) return;
  doodleHistory.push(canvas.toDataURL());
  if (doodleHistory.length > DOODLE_MAX_HISTORY) doodleHistory.shift();
};

// 撤销
const undoDoodle = () => {
  if (doodleHistory.length <= 1) return;
  doodleHistory.pop();
  const prev = doodleHistory[doodleHistory.length - 1];
  const canvas = doodleCanvasRef.value;
  if (!canvas || !doodleCtx) return;
  const img = new Image();
  img.onload = () => {
    doodleCtx!.clearRect(0, 0, canvas.width, canvas.height);
    doodleCtx!.drawImage(img, 0, 0);
  };
  img.src = prev;
};

// 清空涂鸦（保留背景图）
const clearDoodle = () => {
  if (!doodleImageLoaded) return;
  const canvas = doodleCanvasRef.value;
  if (!canvas || !doodleCtx) return;
  const img = new Image();
  img.crossOrigin = 'anonymous';
  img.onload = () => {
    doodleCtx!.clearRect(0, 0, canvas.width, canvas.height);
    doodleCtx!.drawImage(img, 0, 0, canvas.width, canvas.height);
    saveDoodleHistory();
  };
  img.src = doodleImageUrl.value;
};

// 确认涂鸦：导出合成图，emit 给 vueFlow 创建子节点
const confirmDoodle = () => {
  const canvas = doodleCanvasRef.value;
  if (!canvas) {
    exitDoodle();
    return;
  }
  const dataUrl = canvas.toDataURL('image/png');
  emit('doodle-confirm', dataUrl);
  exitDoodle();
};

// 取消涂鸦
const cancelDoodle = () => {
  exitDoodle();
};

// 退出涂鸦模式：清理事件 + 状态
const exitDoodle = () => {
  const canvas = doodleCanvasRef.value;
  if (canvas) {
    canvas.removeEventListener('mousedown', onDoodleMouseDown);
    canvas.removeEventListener('mousemove', onDoodleMouseMove);
    canvas.removeEventListener('mouseup', onDoodleMouseUp);
    canvas.removeEventListener('mouseleave', onDoodleMouseUp);
    canvas.removeEventListener('touchstart', onDoodleTouchStart);
    canvas.removeEventListener('touchmove', onDoodleTouchMove);
    canvas.removeEventListener('touchend', onDoodleTouchEnd);
  }
  doodleHistory.length = 0;
  doodleImageLoaded = false;
  doodleCtx = null;
  doodleMode.value = false;
  doodleActive.value = false;
};

// 组件卸载时兜底清理
onBeforeUnmount(exitDoodle);

// ============ 涂鸦信号监听 ============
watch(doodleSignal, () => {
  if (props.selected && hasImage.value && !doodleMode.value && !cropMode.value) {
    enterDoodle();
  }
});
watch(doodleConfirmSignal, () => {
  if (doodleMode.value) confirmDoodle();
});
watch(doodleCancelSignal, () => {
  if (doodleMode.value) cancelDoodle();
});
watch(doodleUndoSignal, () => {
  if (doodleMode.value) undoDoodle();
});
watch(doodleClearSignal, () => {
  if (doodleMode.value) clearDoodle();
});
</script>

<template>
  <div class="image-node">
    <NodeTitle :data="data" />
    <!-- 选中时右上角显示上传/替换按钮（子节点不显示，裁剪时隐藏） -->
    <button
      v-if="selected && !isChild && !cropMode && !doodleMode"
      class="upload-btn absolute top-30 right-8 z-1 round-4 color-fff fs-12 pointer border-none opacity-80"
      @click.stop="triggerUpload"
    >
      {{ uploading ? '上传中…' : hasImage ? '⬆替换' : '⬆上传' }}
    </button>
    <div class="image-row">
      <!-- 有内容时隐藏 target 柄的 + 按钮，但子节点仍需 target 柄供连线 -->
      <HandleComponents
        v-if="!hasImage"
        :position="position"
        :id="id"
        :data="data"
        type="target"
        handleType="image"
        @node-added="emit('node-added', $event)"
      />
      <Handle v-else-if="isChild" type="target" :position="Position.Left" class="plain-target-handle" />

      <!-- 节点固定尺寸：有图则撑满显示，无图则点击上传占位 -->
      <div
        class="image-display"
        :class="{ 'crop-mode': cropMode, 'doodle-mode': doodleMode }"
        :style="
          cropMode
            ? { width: cropDisplayWidth + 'px', height: cropDisplayHeight + 'px' }
            : doodleMode
              ? { width: doodleDisplayWidth + 'px', height: doodleDisplayHeight + 'px' }
              : {}
        "
      >
        <!-- 正常模式：显示图片 -->
        <img v-if="hasImage && !cropMode && !doodleMode" :src="imageUrl" class="image-el" draggable="false" />
        <div v-else-if="!hasImage && !cropMode && !doodleMode" class="image-empty">
          <div class="empty-icon">🖼</div>
          <div class="empty-text">暂无图片</div>
          <div v-if="data?.label" class="empty-label">{{ data.label }}</div>
        </div>

        <!-- 裁剪模式：原地替换为 Cropper -->
        <Cropper
          v-if="cropMode"
          :key="cropperKey"
          :originImage="cropImageUrl"
          :enableCut="isFreeMode"
          :defaultCutWidth="cutWidth"
          :defaultCutHeight="cutHeight"
          :showResolution="true"
          class="crop-el"
          @getImg="onGetImg"
        />

        <!-- 涂鸦模式：原地替换为 canvas（nodrag 阻止节点拖动） -->
        <canvas v-if="doodleMode" ref="doodleCanvasRef" class="doodle-el nodrag"></canvas>
      </div>

      <HandleComponents
        v-if="!(cropMode || doodleMode)"
        :position="position"
        :id="id"
        :data="data"
        type="source"
        handleType="image"
        @node-added="emit('node-added', $event)"
      />
    </div>
  </div>
</template>

<style scoped lang="scss">
.vue-flow__handle {
  background: #000;
  border: none;
  box-shadow: 0 0 0 1px #fff;
  width: 8px;
  height: 8px;
  line-height: 6px;
  font-size: 8px;
  color: #fff;
}

/* 子节点有内容时的纯连线柄，不显示 + 按钮 */
.plain-target-handle {
  width: 8px !important;
  height: 8px !important;
  background: #000 !important;
  border: none !important;
  box-shadow: 0 0 0 1px #fff !important;
  border-radius: 50% !important;
}

.image-node {
  position: relative;
  display: flex;
  flex-direction: column;
  --node-bg: #1f1f1f;
  --node-border: #3a3a3a;
  --node-border-hover: #555;
  --node-w: 260px;
  --node-h: 180px;
}

.image-row {
  display: flex;
  align-items: center;
}

/* 节点固定区域 */
.image-display {
  position: relative;
  width: var(--node-w);
  height: var(--node-h);
  box-sizing: border-box;
  background: var(--node-bg);
  // border: 1px solid var(--node-border);
  border-radius: 6px;
  overflow: hidden;
  cursor: move;
  transition:
    width 0.2s ease,
    height 0.2s ease,
    border-color 0.2s;
  user-select: none;

  &:hover {
    border-color: var(--node-border-hover);
  }

  &.crop-mode {
    cursor: default;
    border-color: #4a6cf7;
    // 禁用 width/height 过渡，确保 Cropper onMounted 时 getComputedStyle 读到最终尺寸
    transition: border-color 0.2s;
  }
}

/* 图片撑满节点区域：cover 填充，可能裁切以铺满 */
.image-el {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* 裁剪模式下 Cropper 撑满容器 */
.crop-el {
  width: 100%;
  height: 100%;
}

/* 涂鸦模式下 canvas 撑满容器 */
.doodle-el {
  width: 100%;
  height: 100%;
  cursor: crosshair;
  display: block;
  background: #f5f5f5;
}

.image-display.doodle-mode {
  cursor: default;
  border-color: #4a6cf7;
  transition: border-color 0.2s;
}

/* 无图占位 */
.image-empty {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: #777;
  border: 1px dashed var(--node-border);

  .empty-icon {
    font-size: 26px;
    color: #555;
  }
  .empty-text {
    font-size: 13px;
  }
  .empty-label {
    font-size: 12px;
    color: #666;
  }
}

/* 选中时右上角上传按钮 */
.upload-btn {
  padding: 4px 8px 6px 8px;
  background: #3f3f3f;
  white-space: nowrap;
  transition: background 0.2s;

  &:hover {
    background: #555;
  }
}
</style>
