<template>
	<div class="image-clip" :class="{ 'is-product': isProduct }" :data-crop-type="type || ''">
		<div class="image-clip-body" :class="{ 'is-cover': type === 'cover' }" ref="imageClipBody">
			<VueCropper
				ref="cropper"
				:img="url"
				crossOrigin="Anonymous"
				:autoCrop="true"
				:fixedNumber="ratio"
				:fixed="isProduct ? type !== 'apparel' : true"
				movable
				@realTime="onChange"
				outputType="png"
			></VueCropper>
		</div>
		<div class="clip_opterate">
			<div class="item" @click="rotateLeft()">
				<span class="icon iconfont icon-chexiao operate_icon"></span>
			</div>
			<div class="item" @click="rotateRight()">
				<span class="icon iconfont icon-chexiao operate_icon icon_chexiao_sec"></span>
			</div>
			<div class="item" @click="changeScale(-0.1)">
				<span class="operate_icon icon_font">-</span>
			</div>
			<div class="item" @click="changeScale(0.1)">
				<span class="operate_icon icon_font">+</span>
			</div>
			<div class="item" @click="refreshCrop()">
				<span class="icon iconfont icon-shuaxin operate_icon"></span>
			</div>
		</div>
	</div>
</template>
<script setup>
	import { ref, useAttrs, onMounted, onBeforeUnmount, computed, nextTick, watch } from "vue"
	import "vue-cropper/dist/index.css"
	import { VueCropper } from "vue-cropper"
	const props = defineProps({
		url: {
			type: String,
			default: ""
		},
		ratio: {
			type: Array,
			default: () => [1, 1]
		},
		isProduct: {
			type: Boolean,
			default: false
		},
		fixedBox: {
			type: Boolean,
			default: true
		},
		type: {
			type: String,
			default: () => ""
		}
	})
	const attrs = useAttrs()

	const autoCropHeight = computed(() => {
		let height = 426
		if (props.type === "cover") height = 375
		else if (props.type === "apparel") height = 320
		return height
	})

	const onChange = (data) => {
		if (attrs.onChange) {
			getCropUrl().then((url) => attrs.onChange(url))
		}
	}
	const cropper = ref(null)
	const imageClipBody = ref(null)
	let injectLabelFrame = 0
	const observer = new ResizeObserver((entries) => {
		refreshCrop()
	})

	const clearCropLabels = (cropperBox) => {
		if (!cropperBox) return
		cropperBox.querySelectorAll(".cropper-line-label").forEach((node) => node.remove())
	}

	const createCropLabel = ({ text, top, className }) => {
		const label = document.createElement("div")
		label.className = `cropper-line-label ${className}`
		label.textContent = text
		label.style.top = top
		label.style.left = className === "label-v" ? "50%" : "0"
		label.style.transform =
			className === "label-v" ? "translate(-50%, -50%)" : "translateY(-50%)"
		return label
	}

	const cropLabelMap = {
		cover: [
			{ text: "crown", top: "2.67%", className: "label-h" },
			{ text: "hip line", top: "63.47%", className: "label-h" },
			{ text: "mid-thigh", top: "92.8%", className: "label-h" },
			{ text: "center", top: "0", className: "label-v" }
		],
		mainProductImage: [
			{ text: "crown", top: "2.67%", className: "label-h" },
			{ text: "footbase", top: "97.6%", className: "label-h" },
			{ text: "center", top: "0", className: "label-v" }
		],
		sketch: [
			{ text: "crown", top: "2.67%", className: "label-h" },
			{ text: "footbase", top: "97.6%", className: "label-h" },
			{ text: "center", top: "0", className: "label-v" }
		],
		apparel: [{ text: "center", top: "0", className: "label-v" }]
	}

	const injectCropLabel = () => {
		const cropperBox = imageClipBody.value?.querySelector(".cropper-view-box")

		if (!cropperBox) return false

		clearCropLabels(cropperBox)
		;(cropLabelMap[props.type] || []).forEach((config) => {
			cropperBox.appendChild(createCropLabel(config))
		})

		return true
	}

	const scheduleInjectCropLabel = (retry = 0) => {
		cancelAnimationFrame(injectLabelFrame)
		injectLabelFrame = requestAnimationFrame(() => {
			if (!injectCropLabel() && retry < 10) {
				scheduleInjectCropLabel(retry + 1)
			}
		})
	}

	onMounted(() => {
		observer.observe(imageClipBody.value)
		scheduleInjectCropLabel()
	})
	onBeforeUnmount(() => {
		observer.disconnect()
		cancelAnimationFrame(injectLabelFrame)
	})
	const rotateLeft = () => {
		cropper.value.rotateLeft()
	}
	const rotateRight = () => {
		cropper.value.rotateRight()
	}
	const refreshCrop = () => {
		cropper.value.refresh()
	}
	const changeScale = (num = 1) => {
		cropper.value.changeScale(num)
	}
	const getCropUrl = () => {
		return new Promise((resolve, reject) => {
			cropper.value.getCropData(resolve)
		})
	}
	const getCropBlob = () => {
		return new Promise((resolve, reject) => {
			cropper.value.getCropBlob(resolve)
		})
	}

	watch(
		[() => props.type, () => props.url],
		async () => {
			await nextTick()
			scheduleInjectCropLabel()
		},
		{ flush: "post" }
	)

	defineExpose({
		getCropUrl,
		getCropBlob
	})
</script>
<style lang="less" scoped>
	.image-clip {
		width: 100%;
		height: 100%;
		display: flex;
		flex-direction: column;
		// height: 100%;
		background: #fff;
		border-radius: calc(2rem * 1.2);
		padding: calc(1.3rem * 1.2) calc(1.3rem * 1.2) calc(2rem * 1.2);
		box-sizing: border-box;

		.image-clip-body {
			width: 100%;
			height: calc(40rem * 1.2);
			// height: 53rem;
			background: yellow;
			:deep(.cropper-box) {
				.cropper-box-canvas {
					background-color: #ffffff;

					// img {
					// 	height: 100%;
					// }
				}
			}
			&.is-cover {
				:deep(.vue-cropper) {
					overflow: hidden;
				}
				// :deep(.cropper-box-canvas) {
				// 	width: 31.1rem !important;
				// 	left: 50% !important;
				// 	transform: translateX(-50%) !important;
				// 	img {
				// 		display: none;
				// 	}
				// }
			}
		}

		.clip_opterate {
			margin: calc(2.7rem * 1.2) auto 0;
			border-radius: calc(1.6rem * 1.2);
			display: flex;
			overflow: hidden;
			border: 1px solid #e2e2e4;
			width: calc(24rem * 1.2);

			.item {
				width: calc(4.7rem * 1.2);
				height: calc(4rem * 1.2);
				display: flex;
				align-items: center;
				justify-content: center;
				border-right: 0.1rem solid #e6e8ea;
				cursor: pointer;

				.icon_chexiao_sec {
					transform: rotateY(180deg); /* 垂直镜像翻转 */
				}

				.operate_icon {
					font-size: calc(1.8rem * 1.2);
					color: rgba(102, 102, 102, 1);
					font-weight: bold;
				}

				.icon_font {
					font-size: calc(2.5rem * 1.2);
					position: relative;
					top: calc(-0.3rem * 1.2);
					user-select: none;
				}

				.icon-shuaxin {
					font-size: calc(1.4rem * 1.2);
				}

				&:last-child {
					border: none;
				}
			}
		}

		&.is-product {
			.image-clip-body {
				width: 45.7rem;
				height: 45.7rem;
				:deep(.cropper-modal) {
					background: transparent;
				}
				:deep(.vue-cropper .cropper-view-box) {
					position: relative;
					overflow: visible !important;
					/* 原有的蓝色边框（outline）由组件控制，这里不干涉 */
				}

				:deep(.vue-cropper .cropper-view-box::after) {
					content: "";
					position: absolute;
					top: 0;
					left: 0;
					width: 100%;
					height: 100%;
					pointer-events: none;
					z-index: 9; /* 位于图片之上，但在控制点之下 */
					background-image: none;
					background-repeat: no-repeat;
				}
			}

			&[data-crop-type="cover"] {
				.image-clip-body {
					:deep(.vue-cropper .cropper-view-box::after) {
						background-image:
							linear-gradient(to right, #4ba5ff 50%, transparent 50%),
							linear-gradient(to right, #4ba5ff 50%, transparent 50%),
							linear-gradient(to right, #4ba5ff 50%, transparent 50%),
							linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
						background-repeat: repeat-x, repeat-x, repeat-x, repeat-y;
						background-size:
							8px 1px,
							8px 1px,
							8px 1px,
							1px 8px;
						background-position:
							0 2.67%,
							0 63.47%,
							0 92.8%,
							50% 0;
					}
				}
			}

			&[data-crop-type="mainProductImage"],
			&[data-crop-type="sketch"] {
				.image-clip-body {
					:deep(.vue-cropper .cropper-view-box::after) {
						background-image:
							linear-gradient(to right, #4ba5ff 50%, transparent 50%),
							linear-gradient(to right, #4ba5ff 50%, transparent 50%),
							linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
						background-repeat: repeat-x, repeat-x, repeat-y;
						background-size:
							8px 1px,
							8px 1px,
							1px 8px;
						background-position:
							0 2.67%,
							0 97.6%,
							50% 0;
					}
				}
			}

			&[data-crop-type="apparel"] {
				.image-clip-body {
					:deep(.vue-cropper .cropper-view-box::after) {
						background-image: linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
						background-repeat: repeat-y;
						background-size: 1px 8px;
						background-position: 50% 0;
					}
				}
			}
		}
	}
</style>

<style lang="less">
	.cropper-line-label {
		position: absolute;
		color: #4ba5ff; /* 统一颜色 */
		font-size: 11px;
		font-weight: bold;
		background: rgba(255, 255, 255); /* 浅色背景确保在深色图片上可见 */
		// padding: 0 4px;
		border-radius: 2px;
		white-space: nowrap;
		pointer-events: none;
		z-index: 10;
		line-height: 1.2;
	}

	/* 水平线名称：放在线段上方 2px */
	.label-h {
		transform: translateY(-100%);
		margin-top: -2px;
		left: 4px;
	}

	/* 垂直线名称：放在裁剪框顶部边缘上方 */
	.label-v {
		transform: translateX(-50%);
		top: -20px;
		left: 50%;
	}
</style>
