import { GlobalConfig } from "../../GlobalConfig";
import { PopsHandler } from "../../handler/PopsHandler";
import { pops } from "../../Pops";
import { popsDOMUtils } from "../../utils/PopsDOMUtils";
import { PopsSafeUtils } from "../../utils/PopsSafeUtils";
import { popsUtils } from "../../utils/PopsUtils";
import { PopsTooltipConfig } from "./config";
import type { PopsToolTipDetails } from "./indexType";

type ToolTipEventTypeName = "MouseEvent" | "TouchEvent";

export class ToolTip {
	$el = {
		$shadowContainer: null as unknown as HTMLDivElement,
		$shadowRoot: null as unknown as ShadowRoot | HTMLElement,
		$toolTip: null as unknown as HTMLElement,
		$content: null as unknown as HTMLElement,
		$arrow: null as unknown as HTMLElement,
	};
	$data = {
		config: null as any as Required<PopsToolTipDetails>,
		guid: null as any as string,
		timeId_close_TouchEvent: <number[]>[],
		timeId_close_MouseEvent: <number[]>[],
	};
	constructor(
		config: Required<PopsToolTipDetails>,
		guid: string,
		ShadowInfo: {
			$shadowContainer: HTMLDivElement;
			$shadowRoot: ShadowRoot | HTMLElement;
		}
	) {
		this.$data.config = config;
		this.$data.guid = guid;
		this.$el.$shadowContainer = ShadowInfo.$shadowContainer;
		this.$el.$shadowRoot = ShadowInfo.$shadowRoot;
		this.show = this.show.bind(this);
		this.close = this.close.bind(this);
		this.toolTipAnimationFinishEvent =
			this.toolTipAnimationFinishEvent.bind(this);
		this.toolTipMouseEnterEvent = this.toolTipMouseEnterEvent.bind(this);
		this.toolTipMouseLeaveEvent = this.toolTipMouseLeaveEvent.bind(this);
		this.init();
	}
	init() {
		let toolTipInfo = this.createToolTip();
		this.$el.$toolTip = toolTipInfo.$toolTipContainer;
		this.$el.$content = toolTipInfo.$toolTipContent;
		this.$el.$arrow = toolTipInfo.$toolTipArrow;

		this.changeContent();
		this.changeZIndex();
		this.changePosition();

		if (!this.$data.config.alwaysShow) {
			this.offEvent();
			this.onEvent();
		}
	}
	/**
	 * 创建提示元素
	 */
	createToolTip() {
		let $toolTipContainer = popsDOMUtils.createElement(
			"div",
			{
				className: "pops-tip",
				innerHTML: /*html*/ `
				<div class="pops-tip-content" style="text-align: center;"></div>
				<div class="pops-tip-arrow"></div>
			`,
			},
			{
				"data-position": this.$data.config.isFixed ? "fixed" : "absolute",
				"data-guid": this.$data.guid,
			}
		);
		/** 内容 */
		let $toolTipContent =
			$toolTipContainer.querySelector<HTMLElement>(".pops-tip-content")!;
		/** 箭头 */
		let $toolTipArrow =
			$toolTipContainer.querySelector<HTMLElement>(".pops-tip-arrow")!;

		// 处理className
		if (
			typeof this.$data.config.className === "string" &&
			this.$data.config.className.trim() !== ""
		) {
			popsDOMUtils.addClassName($toolTipContainer, this.$data.config.className);
		}
		// 添加z-index
		$toolTipContainer.style.zIndex = PopsHandler.handleZIndex(
			this.$data.config.zIndex
		).toString();
		if (this.$data.config.style != null) {
			/* 添加自定义style */
			let cssNode = popsDOMUtils.createElement("style", {
				type: "text/css",
				innerHTML: this.$data.config.style,
			});
			$toolTipContainer.appendChild(cssNode);
		}
		// 处理是否显示箭头元素
		if (!this.$data.config.showArrow) {
			$toolTipArrow.remove();
		}
		return {
			$toolTipContainer: $toolTipContainer,
			$toolTipArrow: $toolTipArrow,
			$toolTipContent: $toolTipContent,
		};
	}
	/**
	 * 获取提示的内容
	 */
	getContent() {
		return typeof this.$data.config.content === "function"
			? this.$data.config.content()
			: this.$data.config.content;
	}
	/**
	 * 修改提示的内容
	 * @param text
	 */
	changeContent(text?: string) {
		if (text == null) {
			text = this.getContent();
		}
		PopsSafeUtils.setSafeHTML(this.$el.$content, text);
	}
	/**
	 * 获取z-index
	 */
	getZIndex() {
		const zIndex = PopsHandler.handleZIndex(this.$data.config.zIndex);
		return zIndex;
	}
	/**
	 * 动态修改z-index
	 */
	changeZIndex() {
		const zIndex = this.getZIndex();
		this.$el.$toolTip.style.setProperty("z-index", zIndex.toString());
	}
	/**
	 * 计算 提示框的位置
	 * @param targetElement 目标元素
	 * @param arrowDistance 箭头和目标元素的距离
	 * @param otherDistance 其它位置的偏移
	 */
	calcToolTipPosition(
		targetElement: HTMLElement,
		arrowDistance: number,
		otherDistance: number
	) {
		let offsetInfo = popsDOMUtils.offset(
			targetElement,
			!this.$data.config.isFixed
		);
		// 目标 宽
		let targetElement_width = offsetInfo.width;
		// 目标 高
		let targetElement_height = offsetInfo.height;
		// 目标 顶部距离
		let targetElement_top = offsetInfo.top;

		// let targetElement_bottom = offsetInfo.bottom;
		// 目标 左边距离
		let targetElement_left = offsetInfo.left;

		// let targetElement_right = offsetInfo.right;

		let toolTipElement_width = popsDOMUtils.outerWidth(this.$el.$toolTip);
		let toolTipElement_height = popsDOMUtils.outerHeight(this.$el.$toolTip);
		/* 目标元素的x轴的中间位置 */
		let targetElement_X_center_pos =
			targetElement_left + targetElement_width / 2 - toolTipElement_width / 2;
		/* 目标元素的Y轴的中间位置 */
		let targetElement_Y_center_pos =
			targetElement_top + targetElement_height / 2 - toolTipElement_height / 2;
		return {
			TOP: {
				left: targetElement_X_center_pos - otherDistance,
				top: targetElement_top - toolTipElement_height - arrowDistance,
				arrow: "bottom",
				motion: "fadeInTop",
			},
			RIGHT: {
				left: targetElement_left + targetElement_width + arrowDistance,
				top: targetElement_Y_center_pos + otherDistance,
				arrow: "left",
				motion: "fadeInRight",
			},
			BOTTOM: {
				left: targetElement_X_center_pos - otherDistance,
				top: targetElement_top + targetElement_height + arrowDistance,
				arrow: "top",
				motion: "fadeInBottom",
			},
			LEFT: {
				left: targetElement_left - toolTipElement_width - arrowDistance,
				top: targetElement_Y_center_pos + otherDistance,
				arrow: "right",
				motion: "fadeInLeft",
			},
		};
	}
	/**
	 * 动态修改tooltip的位置
	 */
	changePosition() {
		let positionInfo = this.calcToolTipPosition(
			this.$data.config.target,
			this.$data.config.arrowDistance,
			this.$data.config.otherDistance
		);
		let positionKey =
			this.$data.config.position.toUpperCase() as any as keyof typeof positionInfo;
		let positionDetail = positionInfo[positionKey];
		if (positionDetail) {
			this.$el.$toolTip.style.left = positionDetail.left + "px";
			this.$el.$toolTip.style.top = positionDetail.top + "px";
			this.$el.$toolTip.setAttribute("data-motion", positionDetail.motion);

			this.$el.$arrow.setAttribute("data-position", positionDetail.arrow);
		} else {
			console.error("不存在该位置", this.$data.config.position);
		}
	}
	/**
	 * 事件绑定
	 */
	onEvent() {
		// 监听动画结束事件
		this.onToolTipAnimationFinishEvent();
		this.onShowEvent();
		this.onCloseEvent();
		this.onToolTipMouseEnterEvent();
		this.onToolTipMouseLeaveEvent();
	}
	/**
	 * 取消事件绑定
	 */
	offEvent() {
		this.offToolTipAnimationFinishEvent();
		this.offShowEvent();
		this.offCloseEvent();
		this.offToolTipMouseEnterEvent();
		this.offToolTipMouseLeaveEvent();
	}
	/**
	 * 添加关闭的timeId
	 * @param type
	 * @param timeId
	 */
	addCloseTimeoutId(type: ToolTipEventTypeName, timeId: number) {
		if (type === "MouseEvent") {
			this.$data.timeId_close_MouseEvent.push(timeId);
		} else {
			this.$data.timeId_close_TouchEvent.push(timeId);
		}
	}
	/**
	 * 清除延迟的timeId
	 * @param type 事件类型
	 */
	clearCloseTimeoutId(type: ToolTipEventTypeName, timeId?: number) {
		let timeIdList =
			type === "MouseEvent"
				? this.$data.timeId_close_MouseEvent
				: this.$data.timeId_close_TouchEvent;
		for (let index = 0; index < timeIdList.length; index++) {
			const currentTimeId = timeIdList[index];
			if (typeof timeId === "number") {
				// 只清除一个
				if (timeId == currentTimeId) {
					clearTimeout(timeId);
					timeIdList.splice(index, 1);
					break;
				}
			} else {
				clearTimeout(currentTimeId);
				timeIdList.splice(index, 1);
				index--;
			}
		}
	}
	/**
	 * 显示提示框
	 */
	show(...args: any[]) {
		let event = args[0] as MouseEvent | TouchEvent;
		let eventType: ToolTipEventTypeName =
			event instanceof MouseEvent ? "MouseEvent" : "TouchEvent";
		this.clearCloseTimeoutId(eventType);

		if (typeof this.$data.config.showBeforeCallBack === "function") {
			let result = this.$data.config.showBeforeCallBack(this.$el.$toolTip);
			if (typeof result === "boolean" && !result) {
				return;
			}
		}
		if (!popsUtils.contains(this.$el.$shadowRoot, this.$el.$toolTip)) {
			// 不在容器中，添加
			this.init();
			popsDOMUtils.append(this.$el.$shadowRoot, this.$el.$toolTip);
		}

		if (!popsUtils.contains(this.$el.$shadowContainer)) {
			// 页面不存在Shadow，添加
			if (typeof this.$data.config.beforeAppendToPageCallBack === "function") {
				this.$data.config.beforeAppendToPageCallBack(
					this.$el.$shadowRoot,
					this.$el.$shadowContainer
				);
			}
			popsDOMUtils.append(document.body, this.$el.$shadowContainer);
		}

		// 更新内容
		this.changeContent();
		// 更新tip的位置
		this.changePosition();
		if (typeof this.$data.config.showAfterCallBack === "function") {
			this.$data.config.showAfterCallBack(this.$el.$toolTip);
		}
	}
	/**
	 * 绑定 显示事件
	 */
	onShowEvent() {
		popsDOMUtils.on(
			this.$data.config.target,
			this.$data.config.triggerShowEventName,
			this.show,
			this.$data.config.eventOption
		);
	}
	/**
	 * 取消绑定 显示事件
	 */
	offShowEvent() {
		popsDOMUtils.off(
			this.$data.config.target,
			this.$data.config.triggerShowEventName,
			this.show,
			{
				capture: true,
			}
		);
	}
	/**
	 * 关闭提示框
	 */
	close(...args: any[]) {
		let event = args[0] as MouseEvent | TouchEvent;
		let eventType: ToolTipEventTypeName =
			event instanceof MouseEvent ? "MouseEvent" : "TouchEvent";
		// 只判断鼠标事件
		// 其它的如Touch事件不做处理
		if (event && event instanceof MouseEvent) {
			let $target = event.composedPath()[0];
			// 如果是目标元素的子元素/tooltip元素的子元素触发的话，那就不管
			if ($target != this.$data.config.target && $target != this.$el.$toolTip) {
				return;
			}
		}
		if (typeof this.$data.config.closeBeforeCallBack === "function") {
			let result = this.$data.config.closeBeforeCallBack(this.$el.$toolTip);
			if (typeof result === "boolean" && !result) {
				return;
			}
		}
		if (
			this.$data.config.delayCloseTime == null ||
			(typeof this.$data.config.delayCloseTime === "number" &&
				this.$data.config.delayCloseTime <= 0)
		) {
			this.$data.config.delayCloseTime = 100;
		}
		let timeId = setTimeout(() => {
			// 设置属性触发关闭动画
			this.clearCloseTimeoutId(eventType, timeId);
			if (this.$el.$toolTip == null) {
				// 已清除了
				return;
			}
			this.$el.$toolTip.setAttribute(
				"data-motion",
				this.$el.$toolTip
					.getAttribute("data-motion")!
					.replace("fadeIn", "fadeOut")
			);
		}, this.$data.config.delayCloseTime);
		this.addCloseTimeoutId(eventType, timeId);
		if (typeof this.$data.config.closeAfterCallBack === "function") {
			this.$data.config.closeAfterCallBack(this.$el.$toolTip);
		}
	}
	/**
	 * 绑定 关闭事件
	 */
	onCloseEvent() {
		popsDOMUtils.on(
			this.$data.config.target,
			this.$data.config.triggerCloseEventName,
			this.close,
			this.$data.config.eventOption
		);
	}
	/**
	 * 取消绑定 关闭事件
	 */
	offCloseEvent() {
		popsDOMUtils.off(
			this.$data.config.target,
			this.$data.config.triggerCloseEventName,
			this.close,
			{
				capture: true,
			}
		);
	}
	/**
	 * 销毁元素
	 */
	destory() {
		if (this.$el.$toolTip) {
			this.$el.$shadowRoot.removeChild(this.$el.$toolTip);
		}
		// @ts-ignore
		this.$el.$toolTip = null;
		// @ts-ignore
		this.$el.$arrow = null;
		// @ts-ignore
		this.$el.$content = null;
	}
	/**
	 * 动画结束事件
	 */
	toolTipAnimationFinishEvent() {
		if (!this.$el.$toolTip) {
			return;
		}
		if (this.$el.$toolTip.getAttribute("data-motion")!.includes("In")) {
			return;
		}
		this.destory();
	}
	/**
	 * 监听tooltip的动画结束
	 */
	onToolTipAnimationFinishEvent() {
		popsDOMUtils.on(
			this.$el.$toolTip,
			popsDOMUtils.getAnimationEndNameList(),
			this.toolTipAnimationFinishEvent
		);
	}
	/**
	 * 取消tooltip监听动画结束
	 */
	offToolTipAnimationFinishEvent() {
		popsDOMUtils.off(
			this.$el.$toolTip,
			popsDOMUtils.getAnimationEndNameList(),
			this.toolTipAnimationFinishEvent
		);
	}
	/**
	 * 鼠标|触摸进入事件
	 */
	toolTipMouseEnterEvent() {
		this.clearCloseTimeoutId("MouseEvent");
		this.clearCloseTimeoutId("TouchEvent");
		// 重置动画状态
		// this.$el.$toolTip.style.animationPlayState = "paused";
		// if (parseInt(getComputedStyle(toolTipElement)) > 0.5) {
		// toolTipElement.style.animationPlayState = "paused";
		// }
	}
	/**
	 * 监听鼠标|触摸事件
	 */
	onToolTipMouseEnterEvent() {
		this.clearCloseTimeoutId("MouseEvent");
		this.clearCloseTimeoutId("TouchEvent");
		popsDOMUtils.on(
			this.$el.$toolTip,
			"mouseenter touchstart",
			this.toolTipMouseEnterEvent,
			this.$data.config.eventOption
		);
	}
	/**
	 * 取消监听鼠标|触摸事件
	 */
	offToolTipMouseEnterEvent() {
		popsDOMUtils.off(
			this.$el.$toolTip,
			"mouseenter touchstart",
			this.toolTipMouseEnterEvent,
			this.$data.config.eventOption
		);
	}
	/**
	 * 鼠标|触摸离开事件
	 */
	toolTipMouseLeaveEvent(event: MouseEvent | PointerEvent) {
		this.close(event);
		// this.$el.$toolTip.style.animationPlayState = "running";
	}
	/**
	 * 监听鼠标|触摸离开事件
	 */
	onToolTipMouseLeaveEvent() {
		popsDOMUtils.on(
			this.$el.$toolTip,
			"mouseleave touchend",
			this.toolTipMouseLeaveEvent,
			this.$data.config.eventOption
		);
	}
	/**
	 * 取消监听鼠标|触摸离开事件
	 */
	offToolTipMouseLeaveEvent() {
		popsDOMUtils.off(
			this.$el.$toolTip,
			"mouseleave touchend",
			this.toolTipMouseLeaveEvent,
			this.$data.config.eventOption
		);
	}
}

export type PopsTooltipResult<T extends PopsToolTipDetails> = {
	guid: string;
	config: T;
	$shadowContainer: HTMLDivElement;
	$shadowRoot: ShadowRoot;
	toolTip: typeof ToolTip.prototype;
};

export class PopsTooltip {
	constructor(details: PopsToolTipDetails) {
		const guid = popsUtils.getRandomGUID();
		// 设置当前类型
		const PopsType = "tooltip";

		let config = PopsTooltipConfig();
		config = popsUtils.assign(config, GlobalConfig.getGlobalConfig());
		config = popsUtils.assign(config, details);
		if (!(config.target instanceof HTMLElement)) {
			throw "config.target 必须是HTMLElement类型";
		}
		config = PopsHandler.handleOnly(PopsType, config);

		const { $shadowContainer, $shadowRoot } = PopsHandler.handlerShadow(config);
		PopsHandler.handleInit($shadowRoot, [
			pops.config.cssText.index,
			pops.config.cssText.anim,
			pops.config.cssText.common,
			pops.config.cssText.tooltipCSS,
		]);

		let toolTip = new ToolTip(config, guid, {
			$shadowContainer,
			$shadowRoot,
		});
		if (config.alwaysShow) {
			/* 总是显示 */
			/* 直接显示 */
			toolTip.show();
		} else {
			/* 事件触发才显示 */
		}

		return {
			guid: guid,
			config: config,
			$shadowContainer: $shadowContainer,
			$shadowRoot: $shadowRoot,
			toolTip: toolTip,
		};
	}
}
