import { Editor, MarkdownView, Notice, Plugin, Menu, Platform, MarkdownFileInfo, TFile, getLanguage } from 'obsidian';
import { Language, TranslationKey, I18n } from './i18n';
import { ContextData, ContextType, DEFAULT_SETTINGS, EasyCopySettings, LinkFormat, BlockIdInsertPosition, CodeBlockBehavior } from './type';
import { EasyCopySettingTab } from './settingTab';
import { BlockIdInputModal } from './blockIdModal';
import { buildHeadingLink, buildBlockLink, buildFileLink, buildExplicitPasteLink } from './linkBuilder';
import { CopyMetadata, buildBlockCopyMetadata, buildHeadingCopyMetadata, buildFileCopyMetadata } from './copyMetadata';
import { decidePasteResolution, shouldOmitAliasForSameFile, shouldRegisterPasteHandler } from './pasteResolution';

const LAST_COPY_META_TTL_MS = 5 * 60 * 1000;

export default class EasyCopy extends Plugin {
	settings: EasyCopySettings;
	i18n: I18n;
	private lastCopyMeta: CopyMetadata | null = null;
	private pasteEventRef: ReturnType<typeof this.app.workspace.on> | null = null;

	async onload() {
		await this.loadSettings();
		
		// 初始化i18n，使用Obsidian的语言设置
		this.initializeI18n();

		/**
		// This creates an icon in the left ribbon.
		const ribbonIconEl = this.addRibbonIcon('copy', 'Easy Copy', (evt: MouseEvent) => {
			// Called when the user clicks the icon.
			new Notice('This is a notice!');
		});
		// Perform additional things with the ribbon
		ribbonIconEl.addClass('easy-copy-ribbon-class');
		 */

		// This adds a simple command that can be triggered anywhere
		this.addCommand({
			id: 'contextual-copy',
			name: this.t('contextual-copy'),
			icon: 'copy-plus',
			editorCallback: (editor: Editor, view: MarkdownView) => {
				// 实现智能复制功能
				void this.contextualCopy(editor, view);
			}
		});

		if (this.settings.addExtraCommands){
			// 新增：复制当前文件链接命令
			this.addCommand({
				id: 'copy-current-file-link',
				name: this.t('copy-current-file-link'),
				icon: 'clipboard-copy',
				callback: () => {
					this.copyCurrentFileLink();
				}
			});

			// 新增：生成当前 Block 的链接
			this.addCommand({
				id: 'generate-current-block-link-auto',
				name: this.t('generate-current-block-link-auto'),
				icon: 'clipboard-pen',
				editorCallback: (editor: Editor, fileInfo: MarkdownFileInfo) => {
					const file = fileInfo.file;
					if (!file){
						new Notice(this.t('no-file'));
						return;
					}
				const filename = file.basename;
				// 自动生成名称
				void this.insertBlockIdAndCopyLink(editor, filename, false);
				}
			});
			
			// 新增：生成当前 Block 的链接
			this.addCommand({
				id: 'generate-current-block-link-manual',
				name: this.t('generate-current-block-link-manual'),
				icon: 'clipboard-pen-line',
				editorCallback: (editor: Editor, fileInfo: MarkdownFileInfo) => {
					const file = fileInfo.file;
					if (!file){
						new Notice(this.t('no-file'));
						return;
					}
				const filename = file.basename;
				// 手动输入名称
				void this.insertBlockIdAndCopyLink(editor, filename, true);
				}
			});
			
		}
		

		// This adds a settings tab so the user can configure various aspects of the plugin
		this.addSettingTab(new EasyCopySettingTab(this.app, this));

		// 注册右键菜单
		this.registerEvent(
			this.app.workspace.on("editor-menu", (menu: Menu, editor: Editor, view: MarkdownView) => {
				// 只有当设置中的 addToMenu 为 true 时才添加菜单项
				if (this.settings.addToMenu) {
					menu.addItem(item => {
						item
							.setTitle(this.t('contextual-copy'))
						.setIcon('copy-slash')
						.onClick(async () => {
							void this.contextualCopy(editor, view);
						});
					})
				}
			})
		);

		// 手动复制（Ctrl+C、右键复制等）时清除 lastCopyMeta，
		// 避免在粘贴非 Easy Copy 内容时被误拦截。
		// navigator.clipboard.writeText() 不会触发 DOM 的 copy 事件，
		// 所以 Easy Copy 自身写入剪贴板时不受影响。
		// 注意：其他也使用 navigator.clipboard.writeText() 的插件会
		// 绕过这个监听器；冲突需要剪贴板文本完全相同，概率极低。
		// 如果需要更稳健的识别机制，可改用自定义 ClipboardItem MIME 类型。
		this.registerDomEvent(activeDocument, 'copy', () => {
			this.lastCopyMeta = null;
		});

		// 粘贴时解析链接路径：启用后拦截粘贴，根据目标文件重新生成链接。
		// 使用 Obsidian 的 editor-paste workspace 事件，
		// 与其他插件保持协作（按注册顺序、遵循 defaultPrevented 协议）。
		// 仅在开关启用时注册，关闭时彻底退出粘贴插件链。
		this.syncPasteHandlerRegistration();
	}

	syncPasteHandlerRegistration(): void {
		if (shouldRegisterPasteHandler(this.settings)) {
			this.registerPasteHandler();
		} else {
			this.unregisterPasteHandler();
		}
	}

	private registerPasteHandler(): void {
		if (this.pasteEventRef) return;
		this.pasteEventRef = this.app.workspace.on('editor-paste', (evt, editor, info) => {
			if (evt.defaultPrevented) return;
			const handled = this.handlePaste(evt, editor, info);
			if (handled) evt.preventDefault();
		});
		this.registerEvent(this.pasteEventRef);
	}

	private unregisterPasteHandler(): void {
		if (!this.pasteEventRef) return;
		this.app.workspace.offref(this.pasteEventRef);
		this.pasteEventRef = null;
		this.lastCopyMeta = null;
	}

	onunload() {

	}

	async loadSettings() {
		this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
	}

	async saveSettings() {
		await this.saveData(this.settings);
	}

	/**
	 * 当剪贴板内容是 Easy Copy 生成的链接时，拦截粘贴并按目标文件重写。
	 * 「跟随 Obsidian 设置」时调用 generateMarkdownLink()（完整支持
	 * 最短/相对/绝对路径风格）；选择明确的 Wiki/Markdown 格式时改用
	 * fileToLinktext()（仅支持最短唯一路径），以尊重用户的格式选择。
	 *
	 * 依照 editor-paste 契约：若有其他处理器已经 preventDefault，则让出事件。
	 * 插件加载顺序决定 handler 的执行顺序——越早启用的插件越先执行。
	 * 如果其他粘贴类插件（如 Linter）抢先处理了事件，本功能会被跳过。
	 * 此时需确保 Easy Copy 在冲突插件之前启用（先禁用再启用即可调整顺序）。
	 */
	private handlePaste(evt: ClipboardEvent, editor: Editor, info: MarkdownFileInfo): boolean {
		const clipboardText = evt.clipboardData?.getData('text/plain');

		// 如果有活跃的 meta 且剪贴板内容匹配，但被其他插件抢先处理了，输出提示
		// （外层已在 defaultPrevented 时 return，此处作为二次保险）
		if (evt.defaultPrevented && this.lastCopyMeta && clipboardText === this.lastCopyMeta.clipboardText) {
			console.log('[Easy Copy] Paste event was already handled by another plugin. Link path resolution skipped. You can adjust plugin load order in the community-plugins.json file inside your vault\'s config folder (' + this.app.vault.configDir + ').');
			return false;
		}

		const decision = decidePasteResolution({
			defaultPrevented: evt.defaultPrevented,
			resolveLinkPathOnPaste: this.settings.resolveLinkPathOnPaste,
			lastCopyMeta: this.lastCopyMeta,
			clipboardText,
			now: Date.now(),
			ttlMs: LAST_COPY_META_TTL_MS,
		});

		if (decision === 'reset-and-skip') {
			this.lastCopyMeta = null;
			return false;
		}
		if (decision === 'skip') return false;

		// decision === 'rewrite'：此时 lastCopyMeta 和 clipboardText 必非空。
		const meta = this.lastCopyMeta!;

		const destFile = info.file;
		if (!destFile) return false;

		// 退化的自引用：同文件粘贴且无锚点会生成 [[]] / [](#) 之类的空链接，
		// 这种情况下让正常粘贴流程接手即可。
		if (meta.subpath === '' && meta.sourceFilePath === destFile.path) return false;

		const sourceFile = this.app.vault.getAbstractFileByPath(meta.sourceFilePath);
		if (!(sourceFile instanceof TFile)) return false;

		try {
			const effectiveFormat = this.getEffectiveLinkFormat();
			const omitAlias = shouldOmitAliasForSameFile({
				effectiveLinkFormat: effectiveFormat,
				sourceFilePath: meta.sourceFilePath,
				destFilePath: destFile.path,
				subpath: meta.subpath,
				alias: meta.alias,
				useHeadingAsDisplayText: this.settings.useHeadingAsDisplayText,
			});

			let link: string;
			if (this.settings.linkFormat === LinkFormat.OBSIDIAN) {
				// generateMarkdownLink 会遵循 vault 配置（useMarkdownLinks +
				// newLinkFormat），完整支持最短/相对/绝对三种路径风格。
				const aliasArg = omitAlias ? undefined : (meta.alias || undefined);
				link = this.app.fileManager.generateMarkdownLink(
					sourceFile,
					destFile.path,
					meta.subpath || undefined,
					aliasArg,
				);
			} else {
				// 用户选择了明确的 Wiki/Markdown：手动拼接以尊重该格式选择。
				// fileToLinktext 只返回最短唯一路径（不支持相对/绝对路径，
				// 这是有意识的权衡）。omitMdExtension=true 必须传入——
				// 默认 false 会带 ".md"，破坏 wiki 链接约定（[[Note.md]]）。
				const path = this.app.metadataCache.fileToLinktext(sourceFile, destFile.path, true);
				link = buildExplicitPasteLink({
					format: effectiveFormat as LinkFormat.WIKILINK | LinkFormat.MDLINK,
					path,
					subpath: meta.subpath,
					alias: meta.alias,
					sameFile: meta.sourceFilePath === destFile.path,
					omitAlias,
				});
			}

			if (meta.isEmbed) {
				link = '!' + link;
			}

			if (link !== clipboardText) {
				editor.replaceSelection(link);
				return true;
			}
			return false;
		} catch {
			// 链接生成失败——让正常粘贴流程继续
			return false;
		}
	}

	/**
	 * 获取本地化文本
	 * @param key 翻译键值
	 * @returns 翻译后的文本
	 */
	public t(key: TranslationKey): string {
		return this.i18n.t(key);
	}

	/**
	 * 检测是否是有效的一行连续文本
	 */
	private isContinuousText(line: string): boolean {
		// 去除首尾空格后，判断是否为空行、标题行或列表项
		return line.trim() !== '' && !line.trim().startsWith('#') && !line.trim().startsWith('- ');
	}

	/**
	 * 检测光标是否在代码块内，如果是，返回代码块内容
	 * @returns ContextData 或 null
	 */
	private detectCodeBlock(editor: Editor): ContextData | null {
		const behavior = this.settings.codeBlockBehavior;
		if (behavior === CodeBlockBehavior.DISABLED) return null;

		const cursor = editor.getCursor();
		const curLine = cursor.line;
		const totalLines = editor.lineCount();

		// 向上搜索开始围栏 ```
		let fenceStart = -1;
		for (let i = curLine; i >= 0; i--) {
			const line = editor.getLine(i);
			if (/^```/.test(line.trimStart())) {
				fenceStart = i;
				break;
			}
		}
		if (fenceStart === -1) return null;

		// 如果 fenceStart 就是光标所在行，光标在 ``` 行上，不算在代码块"内"
		if (fenceStart === curLine) return null;

		// 从 fenceStart 之后向下搜索结束围栏 ```
		let fenceEnd = -1;
		for (let i = fenceStart + 1; i < totalLines; i++) {
			const line = editor.getLine(i);
			if (/^```\s*$/.test(line.trimStart())) {
				fenceEnd = i;
				break;
			}
		}
		if (fenceEnd === -1) return null;

		// 光标必须在 fenceStart 和 fenceEnd 之间（不含两端的 ``` 行）
		if (curLine >= fenceEnd) return null;

		// 根据行为模式决定返回内容
		if (behavior === CodeBlockBehavior.GENERATE_BLOCK_LINK) {
			// 返回 null，让后续的 block ID 逻辑处理
			return null;
		}

		// 收集代码块内容行（不含 ``` 行）
		const contentLines: string[] = [];
		for (let i = fenceStart + 1; i < fenceEnd; i++) {
			contentLines.push(editor.getLine(i));
		}

		if (behavior === CodeBlockBehavior.COPY_WITH_FENCES) {
			// 包含前后 ``` 行
			const fenceStartLine = editor.getLine(fenceStart);
			const fenceEndLine = editor.getLine(fenceEnd);
			const fullBlock = [fenceStartLine, ...contentLines, fenceEndLine].join('\n');
			return {
				type: ContextType.CODEBLOCK,
				curLine: editor.getLine(curLine),
				match: fullBlock,
				range: null,
			};
		}

		// 默认：COPY_CONTENT — 纯文本（不含 ``` 行）
		const content = contentLines.join('\n');
		return {
			type: ContextType.CODEBLOCK,
			curLine: editor.getLine(curLine),
			match: content,
			range: null,
		};
	}

	/**
	 * 检测光标所在行末尾或下 1~2 行开头的 block ID
	 */
	private detectBlockRange(editor: Editor, cursorLine: number): { start: number, end: number } {
		// 如果当前行是列表，那么范围就是当前行（不对，要继续延伸到后面的……只能说开头是定了）
		if (editor.getLine(cursorLine).trim().startsWith('- ')) {
			let end = cursorLine;
			while (end < editor.lineCount() - 1 && this.isContinuousText(editor.getLine(end + 1))) {
				end++;
			}
			return { start: cursorLine, end };
		}

		const totalLines = editor.lineCount();

		let start = cursorLine;
		while (start > 0 && this.isContinuousText(editor.getLine(start - 1))) {
			start--;
		}

		let end = cursorLine;
		while (end < totalLines - 1 && this.isContinuousText(editor.getLine(end + 1))) {
			end++;
		}

		return { start, end };
	}

	/*
	 * 从给定的块里查找 Block ID（最多延伸至下一个空行+下第二行）
	*/
	private detectBlockId(editor: Editor, view: MarkdownView): ContextData | null {
		const cursor = editor.getCursor();
		const { end } = this.detectBlockRange(editor, cursor.line);
		let lastLine = editor.getLine(end);

		// 检查下两行是否为单独的块ID行
		if (end <= editor.lineCount() - 2) {
			const lineAfterBlock = editor.getLine(end + 1);
			if (lineAfterBlock.trim() === '') {
				const possibleBlockIdLine = editor.getLine(end + 2);
				// 判断该行是否为合法的 block ID 行：前面可有空格，必须以 ^ 开头，后面只能是 block id，不允许有其他字符或空格
				if (/^\s*\^[a-zA-Z0-9_-]+$/.test(possibleBlockIdLine)) {
					lastLine = possibleBlockIdLine;
				}
			}
		}

		const match = lastLine.trimEnd().match(/\^([a-zA-Z0-9_-]+)$/);

		if (match) {
			return {
				type: ContextType.BLOCKID,
				curLine: lastLine,
				match: match[1],
				range: [lastLine.lastIndexOf('^'), lastLine.length]
			};
		}
		return null;
	}

	/**
	 * 获取当前行的上下文类型
	 * @param editor 
	 * @param view 
	 * @returns ContextData
	 */
	private determineContextType(editor: Editor, view: MarkdownView): ContextData {
		// Callout 检测函数
		const detectCallout = (): ContextData | null => {
			if (!this.settings.enableCalloutCopy) return null;
			const cursor = editor.getCursor();
			const totalLines = editor.lineCount();
			let start = cursor.line, end = cursor.line;

			// 当前行是否 callout
			if (!editor.getLine(cursor.line).trim().startsWith('>')) return null;

			// 向上收集 callout
			while (start > 0 && editor.getLine(start - 1).trim().startsWith('>')) start--;
			// 向下收集 callout
			while (end + 1 < totalLines && editor.getLine(end + 1).trim().startsWith('>')) end++;

			// 收集所有 callout 行
			const calloutLines: string[] = [];
			for (let i = start; i <= end; i++) {
				calloutLines.push(editor.getLine(i));
			}
			// 去掉 > 和 callout 头部
			const content = calloutLines.map(line => line.replace(/^>\s?/, '').replace(/^\[!.*?\]\s?/, '').trim()).join('\n');
			
			return {
				type: ContextType.CALLOUT,
				curLine: editor.getLine(cursor.line),
				match: content,
				range: [0, content.length]
			};
		};

		// 获取当前文件和光标信息
		const file = view.file;
		if (!file) {
			new Notice(this.t('no-file'));
			return { type: ContextType.NULL, curLine: '', match: null, range: null };
		}

		const cursor = editor.getCursor();
		const curLine = editor.getLine(cursor.line);
		const curCh = cursor.ch;
	
		// 根据光标位置解析内容类型
		const beforeCursor = curLine.slice(0, curCh); // 光标前的内容
		const afterCursor = curLine.slice(curCh); // 光标后的内容


		// iOS 16.4 之前不支持后视（Lookbehinds），但支持前视（Lookaheads）
		// 所以针对 iOS 平台使用只带前视的正则表达式，其他平台使用完整版本
		const italicRegex = Platform.isIosApp ? 
			/(?:\*([^*]+)\*(?!\*)|_([^_]+)_(?!_))/g :  // iOS 版本：支持 * 和 _ 格式，只使用前视
			/(?:(?<!\*)\*([^*]+)\*(?!\*)|(?<!_)_([^_]+)_(?!_))/g;  // 其他平台：支持 * 和 _ 格式，使用前视和后视
		
		const boldRegex = /(?:\*\*([^*]+)\*\*|__([^_]+)__)/g;  // 粗体：支持 ** 和 __ 格式
		
		// 匹配优先级：加粗 > 斜体 > 高亮 > 删除线 > 行内代码 > 行内Latex > 块ID > 双链
		const matchers = [
			{ type: ContextType.BOLD, regex: boldRegex , enable: !this.settings.customizeTargets || this.settings.enableBold},
			// 使用前后断言确保不被包裹在更长的语法中，同时支持 * 和 _ 两种格式
			{ type: ContextType.ITALIC, regex: italicRegex , enable: !this.settings.customizeTargets || this.settings.enableItalic},
			{ type: ContextType.HIGHLIGHT, regex: /==([^=]+)==/g , enable: !this.settings.customizeTargets || this.settings.enableHighlight},
			{ type: ContextType.STRIKETHROUGH, regex: /~~([^~]+)~~/g , enable: !this.settings.customizeTargets || this.settings.enableStrikethrough},
			{ type: ContextType.INLINECODE, regex: /`([^`]+)`/g , enable: !this.settings.customizeTargets || this.settings.enableInlineCode},
			{ type: ContextType.INLINELATEX, regex: /\$([^$]+)\$/g , enable: !this.settings.customizeTargets || this.settings.enableInlineLatex},
			{ type: ContextType.WIKILINK, regex: /\[\[([^\]]+)\]\]/g, enable: !this.settings.customizeTargets || this.settings.enableWikiLink },
		];
	
		for (const matcher of matchers) {
			if (!matcher.enable) continue; // 如果当前类型未启用，则跳过
			const matchInfo = this.getMatchInfo(beforeCursor, afterCursor, matcher.regex);
			if (matchInfo) {
				return {
					type: matcher.type,
					curLine,
					match: matchInfo.content, // 返回内容，不包括语法
					range: matchInfo.range,
				};
			}
		}

		// 检测链接
		if (!this.settings.customizeTargets || this.settings.enableLink) {
			const linkInfo = this.isCursorInLink(beforeCursor, afterCursor);
			if (linkInfo) {
				return {
					type: linkInfo.type,
					curLine,
					match: linkInfo.content,
					range: linkInfo.range,
				};
			}
		}

		// 检测代码块（在 block ID 之前，因为两者会冲突）
		const codeBlockInfo = this.detectCodeBlock(editor);
		if (codeBlockInfo) {
			return codeBlockInfo;
		}

		// 检测 block ID
		const blockIdInfo = this.detectBlockId(editor, view);
		if (blockIdInfo) {
			return blockIdInfo;
		}

		// 检测 Callout
		if (this.settings.enableCalloutCopy && (!this.settings.autoAddBlockId || this.settings.calloutCopyPriority)) {
			const calloutInfo = detectCallout();
			if (calloutInfo) {
				return calloutInfo;
			}
		}
	
		// 如果当前行是标题行，且光标不在其他 Markdown 语法范围内，则返回标题类型
		const headingRegex = /^(#+)\s/; // 标题正则表达式
		if (headingRegex.test(curLine)) {
			const match = curLine.match(headingRegex);
			const headingContent = curLine.replace(headingRegex, '').trim();
			return {
				type: ContextType.HEADING,
				curLine,
				match: headingContent, // 返回内容，不包括语法
				range: match ? [match[0].length, curLine.length] : null,
			};
		}
	
		// 默认返回空值
		return { type: ContextType.NULL, curLine, match: null, range: null };
	}
	
	/**
	 * 获取光标所在的匹配信息
	 * @param beforeCursor 光标前的文本
	 * @param afterCursor 光标后的文本
	 * @param regex 匹配的正则表达式
	 * @returns 匹配信息，包括匹配内容和范围
	 */
	private getMatchInfo(beforeCursor: string, afterCursor: string, regex: RegExp): { content: string; range: [number, number] } | null {
		let match;
		while ((match = regex.exec(beforeCursor + afterCursor)) !== null) {
			const matchStart = match.index;
			const matchEnd = match.index + match[0].length;
	
			// 判断光标是否在匹配范围内
			if (beforeCursor.length >= matchStart && beforeCursor.length <= matchEnd) {
				// 找到第一个非空且非整体匹配的捕获组作为内容
				let content = '';
				for (let i = 1; i < match.length; i++) {
					if (match[i] !== undefined) {
						content = match[i];
						break;
					}
				}
				
				return {
					content: content, // 返回内容，不包括语法
					range: [matchStart, matchEnd],
				};
			}
		}
		return null;
	}

	private isCursorInLink(beforeCursor: string, afterCursor: string): {type: ContextType.LINKTITLE | ContextType.LINEURL, content: string, range: [number, number]} | null {
		// 匹配链接的正则表达式
		const linkRegex = /\[([^\]]*?)\]\(([^)]*?)\)/g;

		const fullText = beforeCursor + afterCursor;
		let match: RegExpExecArray | null;
		while ((match = linkRegex.exec(fullText)) !== null) {
			const linkStart = match.index; // 链接的起始位置
			const linkEnd = linkStart + match[0].length; // 链接的结束位置
	
			// 光标位置
			const cursorPos = beforeCursor.length;
	
			// 判断光标是否在当前链接范围内
			if (cursorPos >= linkStart && cursorPos <= linkEnd) {
				const bracketStart = linkStart + 1; // `[` 后的位置
				const bracketEnd = linkStart + match[1].length + 1; // `]` 的位置
				const parenStart = bracketEnd + 2; // `(` 后的位置
				const parenEnd = parenStart + match[2].length; // `)` 的位置
	
				// 判断光标在 [] 还是 ()
				if (cursorPos >= bracketStart && cursorPos <= bracketEnd) {
					return { type: ContextType.LINKTITLE, content: match[1], range: [bracketStart, bracketEnd] };
				} else if (cursorPos >= parenStart && cursorPos <= parenEnd) {
					return { type: ContextType.LINEURL, content: match[2], range: [parenStart, parenEnd] };
				}
			}
		}

		// 如果光标不在任何链接中，返回 null
		return null;
	}

	/**
	 * 智能复制功能：根据光标位置复制不同类型的内容
	 * 支持复制行内代码、块ID链接和标题链接
	 */
	private async contextualCopy(editor: Editor, view: MarkdownView): Promise<void> {
		// 获取当前文件和光标信息
		const file = view.file;
		if (!file) {
			new Notice(this.t('no-file'));
			return;
		}
		
		// 获取文件名（去除.md后缀）
		const filename = file.basename;
    
		// 获取当前行的上下文类型
		const contextType = this.determineContextType(editor, view);
		// console.log('contextType:', contextType);

		// Generate Block ID （自动生成 Block ID）
		if (contextType.type == ContextType.NULL) {
			// 如果启用自动添加 Block ID
			if (this.settings.autoAddBlockId) {
				const isManual = this.settings.allowManualBlockId;

				await this.insertBlockIdAndCopyLink(editor, filename, isManual);

				return;
			}
			
			new Notice(this.t('no-content'));

			return;
		}

		switch (contextType.type) {
			case ContextType.BLOCKID:
				this.copyBlockLink(contextType.match!, filename, true, contextType.curLine);
				return;
			case ContextType.BOLD:
				void navigator.clipboard.writeText(contextType.match!);
				if (this.settings.showNotice) {
					new Notice(this.t('bold-copied'));
				}
				return;
			case ContextType.ITALIC:
				void navigator.clipboard.writeText(contextType.match!);
				if (this.settings.showNotice) {
					new Notice(this.t('italic-copied'));
				}
				return;
			case ContextType.HIGHLIGHT:
				void navigator.clipboard.writeText(contextType.match!);
				if (this.settings.showNotice) {
					new Notice(this.t('highlight-copied'));
				}
				return;
			case ContextType.STRIKETHROUGH:
				void navigator.clipboard.writeText(contextType.match!);
				if (this.settings.showNotice) {
					new Notice(this.t('strikethrough-copied'));
				}
				return;
			case ContextType.INLINECODE:
				void navigator.clipboard.writeText(contextType.match!);
				if (this.settings.showNotice) {
					new Notice(this.t('inline-code-copied'));
				}
				return;
			case ContextType.INLINELATEX:
				void navigator.clipboard.writeText(contextType.match!);
				if (this.settings.showNotice) {
					new Notice(this.t('inline-latex-copied'));
				}
				return;
			
			case ContextType.LINKTITLE:
				// 复制链接标题
				void navigator.clipboard.writeText(contextType.match!);
				if (this.settings.showNotice) {
					new Notice(this.t('link-text-copied'));
				}
				return;
			case ContextType.LINEURL:
				// 复制链接地址
				void navigator.clipboard.writeText(contextType.match!);
				if (this.settings.showNotice) {
					new Notice(this.t('link-url-copied'));
				}
				return;
			case ContextType.HEADING:
				this.copyHeadingLink(contextType.match!, filename);
				return;
			case ContextType.WIKILINK: {
				// 复制 [[双链]]，可选保留括号
				if (!contextType.match) return;
				let wikiCopyText = contextType.match;
				if (this.settings.keepWikiBrackets) {
					wikiCopyText = `[[${wikiCopyText}]]`;
				} else {
					// 如果有 |别名，去掉|后面的内容
					wikiCopyText = wikiCopyText.split('|')[0];
				}
				void navigator.clipboard.writeText(wikiCopyText);
				if (this.settings.showNotice) {
					new Notice(this.t('wiki-link-copied'));
				}
				return;
			}
			case ContextType.CODEBLOCK: {
				// 复制代码块内容（纯文本或含围栏，由 detectCodeBlock 已决定）
				void navigator.clipboard.writeText(contextType.match ?? '');
				if (this.settings.showNotice) {
					new Notice(this.t('code-block-copied'));
				}
				return;
			}
			case ContextType.CALLOUT: {
				// 复制 Callout 区块纯文本
				const calloutText = contextType.match?.replace(/\n+/g, '\n').replace(/\s+$/g, '');
				void navigator.clipboard.writeText(calloutText ?? '');
				if (this.settings.showNotice) {
					new Notice(this.t('callout-copied'));
				}
				return;
			}
			default:
				break;
		}
	}
	
	/**
	 * 复制块链接
	 */
	private copyBlockLink(content: string, filename: string, useBrief: boolean, firstLine=''): void {
		const blockIdLink = buildBlockLink({
			blockId: content,
			filename,
			useBrief,
			firstLine,
			linkFormat: this.getEffectiveLinkFormat(),
			autoBlockDisplayText: this.settings.autoBlockDisplayText,
			autoEmbedBlockLink: this.settings.autoEmbedBlockLink,
			blockDisplayWordLimit: this.settings.blockDisplayWordLimit,
			blockDisplayCharLimit: this.settings.blockDisplayCharLimit,
		});

		void navigator.clipboard.writeText(blockIdLink);

		// 存储元数据，供粘贴时解析链接路径使用
		const blockFile = this.app.workspace.getActiveFile();
		if (blockFile) {
			this.lastCopyMeta = buildBlockCopyMetadata({
				clipboardText: blockIdLink,
				sourceFilePath: blockFile.path,
				blockId: content,
				useBrief,
				firstLine,
				autoBlockDisplayText: this.settings.autoBlockDisplayText,
				autoEmbedBlockLink: this.settings.autoEmbedBlockLink,
				blockDisplayWordLimit: this.settings.blockDisplayWordLimit,
				blockDisplayCharLimit: this.settings.blockDisplayCharLimit,
			});
		}

		if (this.settings.showNotice) {
			new Notice(this.t('block-id-copied'));
		}
	}

	/**
	 * 复制标题链接
	 */
	private copyHeadingLink(content: string, filename: string): void {
		// 优先使用 frontmatter 属性作为显示文本
		let frontmatterTitle: string | undefined;
		if (this.settings.useFrontmatterAsDisplay) {
			const file = this.app.workspace.getActiveFile?.() ?? (this.app.workspace.getActiveViewOfType?.(MarkdownView)?.file ?? null);
			if (file) {
				const fileCache = this.app.metadataCache.getFileCache(file);
				const frontmatter = fileCache?.frontmatter;
				const key = this.settings.frontmatterKey || 'title';
				if (frontmatter && typeof frontmatter[key] === 'string' && frontmatter[key].trim()) {
					frontmatterTitle = frontmatter[key].trim();
				}
			}
		}

		const { link, isNoteLink } = buildHeadingLink({
			heading: content,
			filename,
			frontmatterTitle,
			linkFormat: this.getEffectiveLinkFormat(),
			useHeadingAsDisplayText: this.settings.useHeadingAsDisplayText,
			headingLinkSeparator: this.settings.headingLinkSeparator,
			strictHeadingMatch: this.settings.strictHeadingMatch,
			simplifiedHeadingToNoteLink: this.settings.simplifiedHeadingToNoteLink,
		});

		void navigator.clipboard.writeText(link);

		// 存储元数据，供粘贴时解析链接路径使用
		const headingFile = this.app.workspace.getActiveFile();
		if (headingFile) {
			this.lastCopyMeta = buildHeadingCopyMetadata({
				clipboardText: link,
				sourceFilePath: headingFile.path,
				heading: content,
				filename,
				frontmatterTitle,
				useHeadingAsDisplayText: this.settings.useHeadingAsDisplayText,
				headingLinkSeparator: this.settings.headingLinkSeparator,
				isNoteLink,
			});
		}

		if (isNoteLink) {
			new Notice(this.t('note-link-simplified'));
		}
		if (this.settings.showNotice) {
			new Notice(this.t(isNoteLink ? 'note-link-copied' : 'heading-copied'));
		}
	}

	/**
	* 复制当前文件链接（支持 Wiki/Markdown 格式）
	*/
	private copyCurrentFileLink(): void {
		// 优先使用 frontmatter 属性作为显示文本
		let displayText: string | undefined;
		if (this.settings.useFrontmatterAsDisplay) {
			const file = this.app.workspace.getActiveFile?.() ?? (this.app.workspace.getActiveViewOfType?.(MarkdownView)?.file ?? null);
			if (file) {
				const fileCache = this.app.metadataCache.getFileCache(file);
				const frontmatter = fileCache?.frontmatter;
				const key = this.settings.frontmatterKey || 'title';
				if (frontmatter && typeof frontmatter[key] === 'string' && frontmatter[key].trim()) {
					displayText = frontmatter[key].trim();
				}
			}
		}

		const file = this.app.workspace.getActiveFile?.() ?? (this.app.workspace.getActiveViewOfType?.(MarkdownView)?.file ?? null);
		if (!file) {
			new Notice(this.t('no-file'));
			return;
		}

		const link = buildFileLink({
			filename: file.basename,
			filePath: file.path,
			displayText,
			linkFormat: this.getEffectiveLinkFormat(),
		});

		void navigator.clipboard.writeText(link);

		// 存储元数据，供粘贴时解析链接路径使用
		this.lastCopyMeta = buildFileCopyMetadata({
			clipboardText: link,
			sourceFilePath: file.path,
			displayText,
		});

		if (this.settings.showNotice) {
			new Notice(this.t('file-link-copied'));
		}
	}

	/**
	 * 插入 Block ID 并复制块链接
	 * @param editor 编辑器实例
	 * @param filename 当前文件名
	 */
	private async insertBlockIdAndCopyLink(editor: Editor, filename: string, isManual=false): Promise<void> {
		let blockId = '';

		if (isManual) {
			// 用 Modal 弹窗获取 Block ID
			const modalBlockId = await new Promise<string | null>((resolve) => {
				new BlockIdInputModal(this.app,
					this.t('modal-block-id'), this.t('modal-block-id-desc'),
					this.t.bind(this),
					(result: string | null) => {
						resolve(result ?? null);
					}
				).open();
			});
			if (!modalBlockId) return;
			blockId = modalBlockId;
		} else {
			// 随机生成
			const randomId = Math.random().toString(36).substring(2, 8);
			blockId = `${randomId}`;
		}

		// —— 新逻辑：定位 block（段落）末尾 ——
		const cursor = editor.getCursor();
		const { start, end } = this.detectBlockRange(editor, cursor.line);
		const firstLine = editor.getLine(start);
		const lastLine = editor.getLine(end);

		// 检查 block 最后一行末是否已有块ID
		if (!/\^[a-zA-Z0-9_-]+$/.test(lastLine.trim())) {
			// 在 block 最后一行末尾插入块ID
			let insertText = '^' + blockId;
			
			// 代码块、引用块、数学块等
			const isSpecialBlock = lastLine.startsWith('> ') || lastLine.startsWith('```') || lastLine.startsWith('$$');
			const needsSeparator = lastLine.trim().length > 0 && !lastLine.endsWith(' ');  // 末尾需要空格分隔符

			// 添加 block id 到末尾——读取设置中的位置
			const insertPosition = this.settings.blockIdInsertPosition;

			if (isSpecialBlock) {
				insertText = "\n" + insertText;
			} else {
				switch (insertPosition) {
					case BlockIdInsertPosition.END_OF_BLOCK:
						if (needsSeparator) {
							insertText = ' ' + insertText;
						}
						break;
					case BlockIdInsertPosition.NEXT_LINE:
						insertText = "\n" + insertText;
						break;
					default:
						insertText = "\n" + insertText;
						break;
				}
			}

			editor.replaceRange(insertText, { line: end, ch: lastLine.length });

			// 如果不是手动输入的，使用简短的显示文本
			const useBrief = !isManual;

			// （生成之后）复制块ID链接
			this.copyBlockLink(blockId, filename, useBrief, firstLine);
		}
	}

	/**
	 * 初始化国际化设置，使用Obsidian的语言设置
	 */
	private initializeI18n(): void {
		// 获取Obsidian的语言设置
		const obsidianLang = this.getObsidianLanguage();
		
		// 根据Obsidian语言设置选择合适的语言
		let language = Language.EN;
		
		if (obsidianLang.startsWith('zh')) {
			// 区分简体中文和繁体中文
			if (obsidianLang === 'zh-tw') {
				language = Language.ZH_TW;
			} else {
				language = Language.ZH;
			}
		}
		
		// 初始化i18n实例
		this.i18n = new I18n(language);
	}
	
	/**
	 * 获取Obsidian的语言设置
	 * @returns Obsidian的语言代码
	 */
	private getObsidianLanguage(): string {
		// 使用 Obsidian 官方 API 获取语言设置
		return getLanguage();
	}

	/**
	 * 获取实际生效的链接格式
	 * 如果用户选择了"跟随 Obsidian 设置"，则读取 Obsidian 的 vault 配置
	 */
	getEffectiveLinkFormat(): LinkFormat {
		if (this.settings.linkFormat !== LinkFormat.OBSIDIAN) {
			return this.settings.linkFormat;
		}
		// 读取 Obsidian 的 useMarkdownLinks 配置
		// @ts-expect-error - getConfig 是非公开但广泛使用的 API
		const useMarkdownLinks = this.app.vault.getConfig('useMarkdownLinks');
		return useMarkdownLinks ? LinkFormat.MDLINK : LinkFormat.WIKILINK;
	}
}
