<!-- uniapp vue3 markdown解析 -->
<template>
	<view class="ua__markdown">
		<rich-text :nodes="parsedContent" type="text" mode="web" space="nbsp"></rich-text>
		<view class="links" v-if="linkTmp != '' || linkMaps.length > 0">
			<view class="links-title">参考链接：</view>
			<template v-for="item in linkMaps">
				<view class="link-item" @click="copyLink(item.href)">{{item.text}}</view>
			</template>
			<view v-if="linkTmp != ''" class="link-item">{{linkTmp}}</view>
		</view>
	</view>
</template>

<script setup>
	import {
		ref,
		watch
	} from 'vue'
	import MarkdownIt from './lib/markdown-it.min.js'
	import hljs from './lib/highlight/uni-highlight.min.js'
	import './lib/highlight/atom-one-dark.css'
	import parseHtml from './lib/html-parser.js'
	const props = defineProps({
		// 解析内容
		source: String,
		showLine: {
			type: [Boolean, String],
			default: true
		}
	})

	// 存储解析后的内容
	const parsedContent = ref('');

	// 临时链接缓存
	const linkTmp = ref('');

	const clearLinkTmpTimeout = ref(null);

	// 存储链接数据映射
	const linkMaps = ref([]);

	// 解析富文本内容，提取并替换a标签
	const parseContent = () => {
		if (!props.source) return
		// 用于生成唯一ID的计数器
		let linkIdCounter = 0;
		let parseNodeStr = parseNodes(props.source);
		parseNodeStr = parseNodeStr.replace(/[\n\r]/g, '')
		linkMaps.value = [];
		const linkLine = /<p[^>]*>(?:<[^>]+>)*\s*参考链接(.*?)<\/p>/gi;
		// 匹配a标签的正则表达式
		const linkRegex = /<a[^>]+href="([^"]+)"[^>]*>(.*?)<\/a>/gi;
		parsedContent.value = parseNodeStr.replace(linkLine, (match, content) => {
			content = content.replace(linkRegex, (match, href, text) => {
				// 存储链接信息
				linkMaps.value.push({
					text: text,
					href: href.replace(/&amp;/g, "&")
				});
				return '';
			});
			linkTmp.value = content.replace(/<[^>]*>/g, '');
			return '';
		});
		if (clearLinkTmpTimeout.value) {
			clearTimeout(clearLinkTmpTimeout.value);
		}
		// 提取单独的链接（使用 exec 循环代替 matchAll，兼容小程序环境）
		const linkRegex2 = /<a[^>]+href="([^"]+)"[^>]*>(.*?)<\/a>/gi;
		let match;
		while ((match = linkRegex2.exec(parsedContent.value)) !== null) {
			linkMaps.value.push({
				text: match[2],
				href: match[1].replace(/&amp;/g, "&")
			});
		}
		clearLinkTmpTimeout.value = setTimeout(() => {
			linkTmp.value = '';
		}, 60);
	}

	// 处理点击事件
	const copyLink = (link) => {
		uni.setClipboardData({
			data: link,
			success: () => {
				uni.showToast({
					title: "复制成功",
					icon: 'none'
				});
			},
			fail: () => {
				uni.showToast({
					title: "复制失败",
					icon: 'none'
				});
			}
		});
	}

	watch(() => props.source, () => {
		parseContent();
	});

	const markdown = MarkdownIt({
		html: true,
		highlight: function(str, lang) {
			let preCode = ""
			try {
				preCode = hljs.highlightAuto(str).value
			} catch (err) {
				preCode = markdown.utils.escapeHtml(str);
			}
			const lines = preCode.split(/\n/).slice(0, -1)
			// 添加自定义行号
			let html = lines.map((item, index) => {
				if (item == '') {
					return ''
				}
				return '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' + item +
					'</li>'
			}).join('')
			if (props.showLine) {
				html = '<ol style="padding: 0px 30px;">' + html + '</ol>'
			} else {
				html = '<ol style="padding: 0px 7px;list-style:none;">' + html + '</ol>'
			}
			copyCodeData.push(str)
			let htmlCode = `<div class="markdown-wrap">`
			// #ifndef MP-WEIXIN
			htmlCode += `<div style="color: #aaa;text-align: right;font-size: 12px;padding:8px;">`
			htmlCode +=
				`${lang}<a class="copy-btn" code-data-index="${copyCodeData.length - 1}" style="margin-left: 8px;">复制代码</a>`
			htmlCode += `</div>`
			// #endif
			htmlCode +=
				`<pre class="hljs" style="padding:10px 8px 0;margin-bottom:5px;overflow: auto;display: block;border-radius: 5px;"><code>${html}</code></pre>`;
			htmlCode += '</div>'
			return htmlCode
		}
	})
	const parseNodes = (value) => {
		if (!value) return
		// 解析<br />到\n
		value = value.replace(/<br>|<br\/>|<br \/>/g, "\n")
		value = value.replace(/&nbsp;/g, " ")
		let htmlString = ''
		if (value.split("```").length % 2) {
			let mdtext = value
			if (mdtext[mdtext.length - 1] != '\n') {
				mdtext += '\n'
			}
			htmlString = markdown.render(mdtext)
		} else {
			htmlString = markdown.render(value)
		}
		
		// 小程序 rich-text 不继承外部CSS，必须使用内联样式
		// 表格样式
		htmlString = htmlString.replace(/<table>/g, `<table style="border-collapse:collapse;width:100%;margin:0.8em 0;border:1px solid #e5e5e5;">`)
		htmlString = htmlString.replace(/<tr>/g, `<tr style="border-top:1px solid #e5e5e5;">`)
		htmlString = htmlString.replace(/<th>/g, `<th style="padding:6px 13px;border:1px solid #e5e5e5;font-weight:600;background-color:#f5f5f5;">`)
		htmlString = htmlString.replace(/<td>/g, `<td style="padding:6px 13px;border:1px solid #e5e5e5;">`)
		htmlString = htmlString.replace(/<hr>|<hr\/>|<hr \/>/g, `<hr style="margin:1em 0;border:0;border-top:1px solid #e5e5e5;">`)
		
		// 列表样式
		htmlString = htmlString.replace(/<ul>/g, `<ul style="padding-left:20px;margin:0.5em 0;list-style-type:disc;">`)
		htmlString = htmlString.replace(/<ol>/g, `<ol style="padding-left:20px;margin:0.5em 0;list-style-type:decimal;">`)
		htmlString = htmlString.replace(/<li>/g, `<li style="line-height:1.6;margin-bottom:0.2em;">`)
		
		// 标题样式
		htmlString = htmlString.replace(/<h1>/g, `<h1 style="font-size:1.8em;margin:1em 0 0.5em;font-weight:bold;line-height:1.3;">`)
		htmlString = htmlString.replace(/<h2>/g, `<h2 style="font-size:1.5em;margin:1em 0 0.5em;font-weight:bold;line-height:1.3;">`)
		htmlString = htmlString.replace(/<h3>/g, `<h3 style="font-size:1.25em;margin:0.8em 0 0.4em;font-weight:bold;line-height:1.3;">`)
		htmlString = htmlString.replace(/<h4>/g, `<h4 style="font-size:1.1em;margin:0.8em 0 0.4em;font-weight:bold;line-height:1.3;">`)
		htmlString = htmlString.replace(/<h5>/g, `<h5 style="font-size:1em;margin:0.6em 0 0.3em;font-weight:bold;line-height:1.3;">`)
		htmlString = htmlString.replace(/<h6>/g, `<h6 style="font-size:0.9em;margin:0.6em 0 0.3em;font-weight:bold;line-height:1.3;">`)
		
		// 段落样式
		htmlString = htmlString.replace(/<p>/g, `<p style="margin:0.5em 0;line-height:1.6;">`)
		
		// 引用块样式
		htmlString = htmlString.replace(/<blockquote>/g, `<blockquote style="padding:10px 15px;margin:0.8em 0;border-left:4px solid #ddd;color:#666;background:#f9f9f9;">`)
		
		// 行内代码样式
		htmlString = htmlString.replace(/<code>(?![\s\S]*<\/code>[\s\S]*<code>)/g, `<code style="padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:3px;font-family:monospace;">`)
		
		// 链接样式
		htmlString = htmlString.replace(/<a /g, `<a style="color:#3498db;text-decoration:underline;" `)
		
		// 强调样式
		htmlString = htmlString.replace(/<strong>/g, `<strong style="font-weight:bold;">`)
		htmlString = htmlString.replace(/<em>/g, `<em style="font-style:italic;">`)

		// #ifndef APP-NVUE
		return htmlString
		// #endif

		// 将htmlString转成htmlArray，反之使用rich-text解析
		// #ifdef APP-NVUE
		return parseHtml(htmlString)
		// #endif
	}

	const extractPlainText = (markdown) => {
		const md = new MarkdownIt({
			// 禁用所有HTML标签渲染
			html: false,
			// 禁用自动链接识别
			linkify: false,
			// 禁用自动换行转换
			breaks: false,
			// 禁用typographer转换（如引号转换）
			typographer: false
		});

		// 自定义渲染器，只返回文本内容
		const originalRender = md.renderer.rules.text;
		md.renderer.rules.text = function(tokens, idx, options, env, self) {
			return originalRender ? originalRender(tokens, idx, options, env, self) : tokens[idx].content;
		};

		// 处理各种token类型
		md.renderer.rules.link_open = md.renderer.rules.link_close =
			md.renderer.rules.strong_open = md.renderer.rules.strong_close =
			md.renderer.rules.em_open = md.renderer.rules.em_close =
			md.renderer.rules.heading_open = md.renderer.rules.heading_close =
			md.renderer.rules.paragraph_open = md.renderer.rules.paragraph_close =
			md.renderer.rules.list_item_open = md.renderer.rules.list_item_close =
			md.renderer.rules.ordered_list_open = md.renderer.rules.ordered_list_close =
			md.renderer.rules.bullet_list_open = md.renderer.rules.bullet_list_close =
			md.renderer.rules.code_block = md.renderer.rules.fence =
			md.renderer.rules.blockquote_open = md.renderer.rules.blockquote_close =
			function(tokens, idx, options, env, self) {
				return '';
			};

		// 处理图片（完全忽略）
		md.renderer.rules.image = function() {
			return '';
		};

		// 渲染并清理结果
		return md.render(markdown)
			.replace(/\s+/g, ' ')
			.trim();
	}

	/**
	 * 复制文字内容
	 */
	const copyContent = () => {
		const content = extractPlainText(props.source);
		uni.setClipboardData({
			data: content,
			success: () => {
				uni.showToast({
					title: '复制成功',
					icon: 'none'
				});
			}
		})
	}

	defineExpose({
		copyContent,
		extractPlainText
	});
</script>

<style lang="scss">
	/* 小程序 rich-text 不继承外部CSS，内部元素样式通过内联方式在 parseNodes 中处理 */
	/* 此处仅保留容器样式和非 rich-text 内的元素样式 */
	.ua__markdown {
		font-size: 14px;
		line-height: 1.6;
		word-break: break-all;
		color: #333;

		.links {
			margin-top: 16rpx;
			
			.links-title {
				color: #101333;
				margin-bottom: 8rpx;
				font-weight: bold;
				font-size: 28rpx;
			}

			.link-item {
				font-size: 24rpx;
				color: #3498db;
				text-decoration: underline;
				margin-bottom: 6rpx;
			}
		}
	}
</style>