<template>
  <div class="code-fun-flex-row code-fun-justify-center code-fun-relative list-item_7" @touchstart="handleContainerTouchStart" @touchmove="handleContainerTouchMove" @touchend="handleContainerTouchEnd">
    <!-- 删除按钮 -->
    <button class="btn_delete" @click.stop="handleDelete" :style="deleteButtonStyle">
      <span>删除</span>
    </button>
    <!-- 便签条 -->
    <div class="code-fun-flex-col code-fun-relative section_17" @click="handlePress" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd" :style="{ transform: `translateX(${slideOffset}px)` }">
      <div class="code-fun-flex-row code-fun-justify-between">
        <!-- 便签编辑时间 -->
        <span class="font_2 text_18">{{ formattedDate }}</span>
        <div class="code-fun-flex-row group_3">
          <!-- 是否置顶状态&置顶按钮 -->
          <img class="image_11 image_29" :src="isTop ? '/assets/icons/drawable-xxhdpi/icon_top_checked.png' : '/assets/icons/drawable-xxhdpi/icon_top_normal.png'" @click.stop="handleTopToggle" />
          <!-- 是否收藏状态&收藏按钮 -->
          <img class="image_26 ml-5" :src="isStarred ? '/assets/icons/drawable-xxhdpi/icon_detail_star_checked.png' : '/assets/icons/drawable-xxhdpi/icon_detail_star_unchecked.png'" @click.stop="handleStarToggle" />
        </div>
      </div>
      <div class="code-fun-flex-row code-fun-justify-between mt-17-5">
        <!-- 便签正文第一行 -->
        <span class="font_3 text_19">{{ displayContent }}</span>
        <!-- 便签中是否存在图片 -->
        <img v-if="hasImage" class="image_28" src="/assets/icons/drawable-xxhdpi/list_item_image_icon.png" />
      </div>
    </div>
    <!-- 便签夹未滑动状态 -->
    <img class="image_27 pos_18" :src="isSliding ? '/assets/icons/drawable-xxhdpi/note_item_clip_up.png' : '/assets/icons/drawable-xxhdpi/note_item_clip_normal.png'" />
  </div>
</template>

<script setup>
import { computed, ref } from 'vue'

const emit = defineEmits(['update:modelValue'])

const props = defineProps({
  content: {
    type: String,
    required: true,
  },
  date: {
    type: String,
    required: true,
  },
  isStarred: {
    type: Boolean,
    default: false,
  },
  isTop: {
    type: Boolean,
    default: false,
  },
  hasImage: {
    type: Boolean,
    default: false,
  },
  onPress: {
    type: Function,
    default: () => {},
  },
  onStarToggle: {
    type: Function,
    default: () => {},
  },
  onTopToggle: {
    type: Function,
    default: () => {},
  },
  onDelete: {
    type: Function,
    default: () => {},
  },
})

// 滑动相关状态
const slideOffset = ref(0)
const startX = ref(0)
const startY = ref(0) // 记录起始Y坐标
const isSliding = ref(false)
const isSlided = ref(false) // 是否已经滑动到阈值
const isScrolling = ref(false) // 是否正在滚动

const formattedDate = computed(() => {
  // 直接返回已经格式化的日期字符串
  // 日期格式化已在父组件中完成
  return props.date
})

// 处理显示内容，过滤HTML标签并只显示第一行
// 用于在便签列表中显示便签的预览内容
const displayContent = computed(() => {
  // 过滤HTML标签，只保留纯文本内容
  let text = props.content.replace(/<[^>]*>/g, '')
  // 处理换行符，统一为\n
  text = text.replace(/\\n/g, '\n')

  // 按换行符分割并获取第一行
  const lines = text.split('\n')

  // 返回第一行内容，如果为空则显示默认文本
  return lines[0]?.trim() || '无内容'
})

// 计算删除按钮的样式
const deleteButtonStyle = computed(() => {
  return {
    opacity: isSlided.value ? 1 : 0,
    pointerEvents: isSlided.value ? 'auto' : 'none',
  }
})

// 滑动阈值（删除按钮宽度）
// 当滑动距离超过此值时，显示删除按钮
const SLIDE_THRESHOLD = 64 // 4rem 转换为 px

// 容器触摸事件处理函数
const handleContainerTouchStart = e => {
  // 阻止事件冒泡到父组件
  e.stopPropagation()
  // 阻止父级滚动容器的滚动行为
  e.stopImmediatePropagation()
}

const handleContainerTouchMove = e => {
  // 阻止事件冒泡到父组件
  e.stopPropagation()
  // 阻止父级滚动容器的滚动行为
  e.stopImmediatePropagation()
}

const handleContainerTouchEnd = e => {
  // 阻止事件冒泡到父组件
  e.stopPropagation()
  // 阻止父级滚动容器的滚动行为
  e.stopImmediatePropagation()
}

// 处理便签点击事件
// 只有在未滑动状态下才触发点击事件，避免与滑动操作冲突
const handlePress = () => {
  // 只有在未滑动状态下才触发点击事件
  if (slideOffset.value === 0 && props.onPress) {
    props.onPress()
  } else if (slideOffset.value !== 0) {
    // 如果当前处于滑动状态，重置滑动状态（收回便签条）
    resetSlideState()
  }
}

// 处理星标切换事件
// 点击星标图标时调用父组件传递的回调函数
const handleStarToggle = () => {
  if (props.onStarToggle) {
    props.onStarToggle()
  }
}

// 处理置顶切换事件
// 点击置顶图标时调用父组件传递的回调函数
const handleTopToggle = () => {
  if (props.onTopToggle) {
    props.onTopToggle()
  }
}

// 处理删除事件
// 点击删除按钮时调用父组件传递的回调函数
const handleDelete = () => {
  // 阻止事件冒泡，避免触发便签条的点击事件
  props.onDelete()
  // 重置滑动状态
  slideOffset.value = 0
  isSliding.value = false
  isSlided.value = false
}

// 重置滑动状态
const resetSlideState = () => {
  slideOffset.value = 0
  isSliding.value = false
  isSlided.value = false
}

// 获取滑动状态
const getSlideState = () => {
  return isSlided.value
}

// 暴露方法给父组件
defineExpose({ resetSlideState, getSlideState })

// 触摸开始事件处理函数
// 记录触摸开始时的X坐标，用于计算滑动距离
const handleTouchStart = e => {
  // 阻止事件冒泡到父组件，防止页面滚动时触发便签滑动
  e.stopPropagation()
  // 阻止父级滚动容器的滚动行为
  e.stopImmediatePropagation()
  // 重置滑动状态
  startX.value = e.touches[0].clientX
  startY.value = e.touches[0].clientY // 记录起始Y坐标
  isSliding.value = false
  isSlided.value = false
  isScrolling.value = false // 重置滚动状态
}

// 触摸移动事件处理函数
// 根据手指移动距离计算便签条的水平偏移量
const handleTouchMove = e => {
  if (!startX.value || !startY.value) return

  const currentX = e.touches[0].clientX
  const currentY = e.touches[0].clientY
  const diffX = currentX - startX.value
  const diffY = currentY - startY.value

  // 如果已经确定是滚动，则不再处理
  if (isScrolling.value) {
    // 阻止事件冒泡到父组件，防止页面滚动时触发便签滑动
    e.stopPropagation()
    return
  }

  // 判断是滚动还是滑动操作
  // 如果Y轴移动距离大于X轴移动距离，则认为是滚动
  if (Math.abs(diffY) > Math.abs(diffX)) {
    isScrolling.value = true
    // 阻止事件冒泡到父组件，防止页面滚动时触发便签滑动
    e.stopPropagation()
    return
  }

  // 只处理右滑动（正值）
  if (diffX > 0) {
    // 阻止事件冒泡到父组件，防止页面滚动时触发便签滑动
    e.stopPropagation()
    // 阻止父级滚动容器的滚动行为
    e.stopImmediatePropagation()

    // 只有当滑动达到一定距离时才阻止页面滚动
    if (diffX > 5) {
      e.preventDefault() // 防止页面滚动
    }

    // 设置滑动状态
    isSliding.value = true

    // 应用阻尼效果，使超过阈值后的滑动更加困难
    let offset = 0
    if (diffX <= SLIDE_THRESHOLD) {
      // 线性滑动，在阈值内正常滑动
      offset = diffX
    } else {
      // 超过阈值后应用阻尼效果，增加滑动阻力
      const excess = diffX - SLIDE_THRESHOLD
      offset = SLIDE_THRESHOLD + excess * 0.03 // 0.03 为阻尼系数
    }

    slideOffset.value = offset
    isSlided.value = offset >= SLIDE_THRESHOLD
  } else if (diffX < 0) {
    // 左滑动，将便签条移回原位
    const offset = Math.max(0, slideOffset.value + diffX)
    slideOffset.value = offset
    isSlided.value = offset >= SLIDE_THRESHOLD

    // 更新 startX 以确保连续滑动的正确性
    startX.value = currentX
  }
}

// 触摸结束事件处理函数
// 根据滑动距离决定便签条的最终位置
const handleTouchEnd = e => {
  if (!startX.value) return

  // 阻止事件冒泡到父组件，防止页面滚动时触发便签滑动
  e?.stopPropagation()
  // 阻止父级滚动容器的滚动行为
  e?.stopImmediatePropagation()

  // 如果滑动超过阈值，保持滑出状态；否则回弹
  if (slideOffset.value >= SLIDE_THRESHOLD) {
    // 保持滑出状态，显示删除按钮
    slideOffset.value = SLIDE_THRESHOLD
    isSlided.value = true
  } else {
    // 回弹到初始位置
    slideOffset.value = 0
    isSliding.value = false
    isSlided.value = false
  }

  // 重置起始位置和滚动状态
  startX.value = 0
  startY.value = 0
  isScrolling.value = false
}
</script>

<style lang="less" scoped>
.mt-17-5 {
  margin-top: -1rem;
}
.btn_delete {
  background: url(/assets/icons/drawable-xxhdpi/btn_slide_delete_normal.png);
  background-size: cover;
  width: 4rem;
  height: 2rem;
  position: absolute;
  top: 50%;
  left: 1rem;
  z-index: 1;
  transform: translate(0, -50%);
  color: white;
  text-align: right;
  border: none;
  padding: 0;
  transition: opacity 0.2s ease;
  span {
    margin-right: 0.7rem;
    font-size: 0.6rem;
    display: block;
  }
}
.list-item_7 {
  width: 100%;
  height: 3.16rem;
  touch-action: pan-y; /* 只允许垂直滚动，禁止水平滑动 */
  .section_17 {
    box-sizing: border-box;
    width: 95%;
    height: 100%;
    background: linear-gradient(to bottom, #fffdf6, #f3eee4);
    z-index: 2;
    padding: 0.44rem 0.69rem 0.88rem 2.09rem;
    box-shadow: 0 2px 2px 1px rgb(0 0 0 / 15%);
    transition: transform 0.2s ease-out;
    border-radius: 3px;
    &:before {
      content: '';
      width: 1.5rem;
      height: 100%;
      position: absolute;
      left: 0;
      top: 0;
      background: linear-gradient(to bottom, #f9f5ee, #eee6dc);
    }
    .font_2 {
      font-size: 0.71rem;
      line-height: 0.71rem;
      color: #c2bdb1;
    }
    .text_18 {
      margin-top: 0.061rem;
      color: #c3beb4;
    }
    .group_3 {
      margin-right: 0.19rem;
      margin-bottom: 0.063rem;
      .image_11 {
        width: 2.2rem;
        height: 2.2rem;
        object-fit: contain;
        position: relative;
        top: -0.15rem;
      }
      .image_29 {
        margin-top: 0.063rem;
      }
      .image_26 {
        width: 1.2rem;
        height: 1.2rem;
      }
    }
    .font_3 {
      font-size: 0.88rem;
      line-height: 0.88rem;
    }
    .text_19 {
      margin-bottom: 0.065rem;
      color: #816d61;
      font-size: 0.9rem;
      line-height: 0.9rem;
      word-break: break-all;
      text-overflow: ellipsis;
      display: -webkit-box;
      -webkit-box-orient: vertical;
      overflow: hidden;
      -webkit-line-clamp: 1;
    }
    .image_28 {
      width: 1.06rem;
      height: 0.91rem;
    }
  }
  .image_27 {
    width: 1.7rem;
    height: 1.7rem;
    object-fit: cover;
  }
  .pos_18 {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translate(0, -50%);
    z-index: 3;
    transition: transform 0.3s ease-out;
  }
}
</style>
