// 引入颜色处理库
import { tinyColor } from '@/uni_modules/lime-color';


// ===================== 类型定义 =====================
/**
 * 加载动画类型
 * circular: 环形加载动画
 * spinner: 旋转器加载动画
 * failed: 失败状态动画
 */
export type LoadingType = 'circular' | 'spinner' | 'failed';

/**
 * 操作类型
 * play: 开始动画
 * failed: 显示失败状态
 * clear: 清除动画
 * destroy: 销毁实例
 */
export type TickType = 'play' | 'failed' | 'clear' | 'destroy' | 'pause'

/**
 * 加载组件配置选项
 * @property type - 初始动画类型
 * @property strokeColor - 线条颜色
 * @property ratio - 尺寸比例
 * @property immediate - 是否立即启动
 */
export type UseLoadingOptions = {
	type : LoadingType;
	strokeColor : string;
	ratio : number;
	immediate ?: boolean;
};

/**
 * 加载组件返回接口
 */
export type UseLoadingReturn = {
	// state : Ref<boolean>;
	// setOptions: (options: UseLoadingOptions) => void
	ratio : 1;
	type : LoadingType;
	color : string;//Ref<string>;
	play : () => void;
	failed : () => void;
	clear : () => void;
	destroy : () => void;
	pause : () => void;
}

/**
 * 画布尺寸信息
 */
export type Dimensions = {
	width : number;
	height : number;
	size : number
}

/**
 * 线段坐标点
 */
type Point = {
	x1 : number
	y1 : number
	x2 : number
	y2 : number
}

/**
 * 画布上下文信息
 */
type LoadingCanvasContext = {
	ctx : Ref<DrawableContext | null>;
	dimensions : Ref<Dimensions>;
	updateDimensions : (el : UniElement) => void;
};

/**
 * 动画参数配置
 */
type AnimationParams = {
	width : number
	height : number
	center : number[] // 元组类型，明确表示两个数值的坐标
	color : string       // 使用Ref类型包裹字符串
	size : number              // 数值类型尺寸
}

// ===================== 动画管理器 =====================
type AnimationFrameHandler = () => boolean;

/**
 * 动画管理类
 * 封装动画的启动/停止逻辑
 */
export class AnimationManager {
	time : number = 1000 / 60 // 默认帧率60fps
	private timer : number = -1;// 定时器ID
	private isDestroyed : boolean = false; // 销毁状态
	private drawFrame : AnimationFrameHandler// 帧绘制函数
	private lastTime : number = 0;
	private isRunning : boolean = false;
	type : LoadingType;
	constructor(drawFrame : AnimationFrameHandler, type : LoadingType = 'circular') {
		this.type = type
		this.drawFrame = drawFrame
	}
	/** 启动动画循环 */
	start() {
		if (this.isRunning) return;
		this.isRunning = true;
		this.lastTime = Date.now();
		let animate : ((task : number) => void) | null = null

		animate = (task : number) => {
			if (!this.isRunning) return;
			if (this.isDestroyed) return;
			const delta = Date.now() - this.lastTime;
			if (delta >= this.time || delta < 10) {
				const shouldContinue : boolean = this.drawFrame();
				this.lastTime = Date.now()
				if (!shouldContinue) {
					this.stop();
					return;
				}
			}
			this.timer = requestAnimationFrame(animate!);
		};

		animate(Date.now());
	}
	/** 停止动画并清理资源 */
	stop() {
		cancelAnimationFrame(this.timer)
		this.isDestroyed = true;
	}
	pause() {
		this.isRunning = false;
		cancelAnimationFrame(this.timer);
	}
}

// ===================== 工具函数 =====================
/**
 * 缓动函数 - 三次缓入缓出
 * @param t 时间系数 (0-1)
 * @returns 计算后的进度值
 */
function easeInOutCubic(t : number) : number {
	return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
}

// ===================== 画布管理 =====================
/**
 * 获取画布上下文信息
 * @param element 画布元素引用
 * @returns 包含画布上下文和尺寸信息的对象
 */
//_element : Ref<UniElement | null>
export function useCanvas() : LoadingCanvasContext {
	const ctx = shallowRef<DrawableContext | null>(null);
	const dimensions = ref<Dimensions>({
		width: 0,
		height: 0,
		size: 0
	});

	const updateDimensions = (el : UniElement) => {
		// const rect = el.getBoundingClientRect();
		// // 鸿蒙尺寸为0时 再次尺寸变化会不渲染
		// if(rect.width == 0) return
		// if(ctx.value == null) {
		// 	ctx.value = el.getDrawableContext() as DrawableContext;
		// }
		// dimensions.value.width = rect.width;
		// dimensions.value.height = rect.height;
		// dimensions.value.size = Math.min(rect.width, rect.height);
		el.getBoundingClientRectAsync()?.then(rect => {
			if (rect.width == 0 || rect.height == 0) return
			ctx.value = el.getDrawableContext() as DrawableContext;
			dimensions.value.width = rect.width;
			dimensions.value.height = rect.height;
			dimensions.value.size = Math.min(rect.width, rect.height);
		})
	};

	return {
		ctx,
		dimensions,
		updateDimensions
	} as LoadingCanvasContext
}

// ===================== 动画创建函数 =====================
/**
 * 创建环形加载动画
 * @param ctx 画布上下文
 * @param animationParams 动画参数
 * @returns 动画管理器实例
 */
function createCircularAnimation(ctx : DrawableContext, animationParams : AnimationParams) : AnimationManager {
	const { size, color, width, height } = animationParams
	let startAngle = 0;   // 起始角度
	let endAngle = 0;     // 结束角度
	let rotate = 0;       // 旋转角度

	// 动画参数配置
	const MIN_ANGLE = 5;          // 最小保持角度
	const ARC_LENGTH = 359.5      // 最大弧长（避免闭合）
	const PI = Math.PI / 180      // 角度转弧度系数
	const SPEED = 0.018           // 动画速度
	const ROTATE_INTERVAL = 0.09  // 旋转增量
	const lineWidth = size / 10;  // 线宽计算
	const x = width / 2           // 中心点X
	const y = height / 2          // 中心点Y
	const radius = size / 2 - lineWidth // 实际绘制半径

	/** 帧绘制函数 */
	const drawFrame = () : boolean => {
		ctx.reset();

		// 绘制圆弧
		ctx.beginPath();
		ctx.arc(
			x,
			y,
			radius,
			startAngle * PI + rotate,
			endAngle * PI + rotate
		);
		ctx.lineWidth = lineWidth;
		ctx.strokeStyle = color;
		ctx.stroke();

		// 角度更新逻辑
		if (endAngle < ARC_LENGTH) {
			endAngle = Math.min(ARC_LENGTH, endAngle + (ARC_LENGTH - MIN_ANGLE) * SPEED);
		} else if (startAngle < ARC_LENGTH) {
			startAngle = Math.min(ARC_LENGTH, startAngle + (ARC_LENGTH - MIN_ANGLE) * SPEED);
		} else {
			// 重置时保留最小可见角度
			startAngle = 0;
			endAngle = MIN_ANGLE;
		}

		rotate = (rotate + ROTATE_INTERVAL) % 360; // 持续旋转并限制范围
		ctx.update()
		return true
	}

	return new AnimationManager(drawFrame)
}

/**
 * 创建旋转器动画
 * @param ctx 画布上下文
 * @param animationParams 动画参数
 * @returns 动画管理器实例
 */
function createSpinnerAnimation(ctx : DrawableContext, animationParams : AnimationParams) : AnimationManager {
	const { size, color, center } = animationParams
	const steps = 12;     // 旋转线条数量
	let step = 0;         // 当前步数
	const lineWidth = size / 10;  // 线宽
	// #ifdef APP-HARMONY
	const length = size / 3.4 - lineWidth;  // 线长
	// #endif
	// #ifndef APP-HARMONY
	const length = size / 3.6 - lineWidth;  // 线长
	// #endif
	const offset = size / 4;      // 距中心偏移
	const [x, y] = center         // 中心坐标

	/** 生成颜色渐变数组 */
	function generateColorGradient(hex : string, steps : number) : string[] {
		const colors : string[] = []
		const _color = tinyColor(hex)

		for (let i = 1; i <= steps; i++) {
			_color.setAlpha(i / steps);
			colors.push(_color.toRgbString());
		}
		return colors
	}

	// 计算颜色渐变
	let colors = computed(() : string[] => generateColorGradient(color, steps))
	/** 帧绘制函数 */
	const drawFrame = () : boolean => {
		ctx.reset();
		for (let i = 0; i < steps; i++) {
			const stepAngle = 360 / steps;    // 单步角度
			const angle = stepAngle * i;      // 当前角度
			const index = (steps + i - step) % steps // 颜色索引
			// const index = (i + step) % steps; 
			// console.log('index', index)
			// 计算线段坐标
			const radian = angle * Math.PI / 180;
			const cos = Math.cos(radian);
			const sin = Math.sin(radian);

			// 绘制线段
			ctx.beginPath();
			ctx.moveTo(x + offset * cos, y + offset * sin);
			ctx.lineTo(x + (offset + length) * cos, y + (offset + length) * sin);
			ctx.lineWidth = lineWidth;
			ctx.lineCap = 'round';
			ctx.strokeStyle = colors.value[index];
			ctx.stroke();
		}
		// step += 1
		step = (step + 1) % steps; // 限制step范围
		ctx.update()
		return true
	}
	return new AnimationManager(drawFrame)
}

/**
 * 计算圆周上指定角度的点的坐标
 * @param centerX 圆心的 X 坐标
 * @param centerY 圆心的 Y 坐标
 * @param radius 圆的半径
 * @param angleDegrees 角度（以度为单位）
 * @returns 包含 X 和 Y 坐标的对象
 */
function getPointOnCircle(
	centerX : number,
	centerY : number,
	radius : number,
	angleDegrees : number
) : number[] {
	// 将角度转换为弧度
	const angleRadians = (angleDegrees * Math.PI) / 180;

	// 计算点的 X 和 Y 坐标
	const x = centerX + radius * Math.cos(angleRadians);
	const y = centerY + radius * Math.sin(angleRadians);

	return [x, y]
}

/**
 * 创建失败状态动画（包含X图标和外围圆圈）
 * @param ctx 画布上下文
 * @param animationParams 动画参数
 * @returns 动画管理器实例
 */
function createFailedAnimation(ctx : DrawableContext, animationParams : AnimationParams) : AnimationManager {

	const { width, height, size, color } = animationParams
	const innerSize = size * 0.8 // 内圈尺寸
	const lineWidth = innerSize / 10; // 线宽
	const lineLength = (size - lineWidth) / 2 // X长度
	const centerX = width / 2;
	const centerY = height / 2;

	const [startX1, startY] = getPointOnCircle(centerX, centerY, lineLength / 2, 180 + 45)
	const [startX2] = getPointOnCircle(centerX, centerY, lineLength / 2, 180 + 90 + 45)
	const angleRadians1 = 45 * Math.PI / 180
	const angleRadians2 = (45 - 90) * Math.PI / 180

	const radius = (size - lineWidth) / 2
	const totalSteps = 36;                // 总动画步数

	function generateSteps(stepsCount : number) : Point[][] {

		const halfStepsCount = stepsCount / 2;
		const step = lineLength / halfStepsCount
		const steps : Point[][] = []
		for (let i = 0; i < stepsCount; i++) {
			const sub : Point[] = []
			const index = i % 18 + 1
			if (i < halfStepsCount) {

				const x2 = Math.sin(angleRadians1) * step * index + startX1
				const y2 = Math.cos(angleRadians1) * step * index + startY

				const start1 = {
					x1: startX1,
					y1: startY,
					x2,
					y2,
				} as Point

				sub.push(start1)
			} else {
				sub.push(steps[halfStepsCount - 1][0])
				const x2 = Math.sin(angleRadians2) * step * index + startX2
				const y2 = Math.cos(angleRadians2) * step * index + startY

				const start2 = {
					x1: startX2,
					y1: startY,
					x2,
					y2,
				} as Point
				sub.push(start2)
			}
			steps.push(sub)
		}

		return steps
	}
	const steps = generateSteps(totalSteps);

	const drawFrame = () : boolean => {
		const drawStep = steps.shift()!
		ctx.reset()
		ctx.lineWidth = lineWidth;
		ctx.strokeStyle = color;

		// 绘制逐渐显示的圆
		ctx.beginPath();
		ctx.arc(centerX, centerY, radius, 0, (2 * Math.PI) * (totalSteps - steps.length) / totalSteps);
		ctx.lineWidth = lineWidth;
		ctx.strokeStyle = color;
		ctx.stroke();

		// 绘制X
		ctx.beginPath();
		drawStep.forEach(item => {
			ctx.beginPath();
			ctx.moveTo(item.x1, item.y1)
			ctx.lineTo(item.x2, item.y2)
			ctx.stroke();
		})
		ctx.update()
		return steps.length != 0
	}
	return new AnimationManager(drawFrame)
}


// ===================== 主Hook函数 =====================
/**
 * 加载动画组合式函数
 * @param element 画布元素引用
 * @returns 加载控制器实例
 */
export function useLoading(
	element : Ref<UniElement | null>,
	// options : UseLoadingOptions
) : UseLoadingReturn {
	const ticks = ref<TickType[]>([]);
	const currentTick = ref<TickType>('clear');
	const currentAnimation = shallowRef<AnimationManager | null>(null);
	const state = reactive<UseLoadingReturn>({
		color: '#000',
		type: 'circular',
		ratio: 1,
		play: () => {
			// if (currentTick.value == 'pause' && currentAnimation.value != null) {
			// 	// 从暂停状态恢复时直接启动动画管理器
			// 	currentAnimation.value?.start()
			// } else {
			// 	// 首次播放或类型变化时重新初始化
			// 	ticks.value.length = 0
			// 	ticks.value.push('play')
			// }
			ticks.value.length = 0
			ticks.value.push('play')
		},
		failed: () => {
			ticks.value.length = 0
			ticks.value.push('failed')
		},
		clear: () => {
			ticks.value.length = 0
			ticks.value.push('clear')
		},
		destroy: () => {
			ticks.value.length = 0
			ticks.value.push('destroy')
		},
		pause: () => {
			ticks.value.push('pause')
		}
	})


	const { ctx, dimensions, updateDimensions } = useCanvas();
	const resizeObserver : UniResizeObserver = new UniResizeObserver((_entries : UniResizeObserverEntry[]) => {
		updateDimensions(element.value!)
	});


	// 计算动画参数
	const animationParams = computed(() : AnimationParams => {
		return {
			width: dimensions.value.width,
			height: dimensions.value.height,
			center: [dimensions.value.width / 2, dimensions.value.height / 2],
			color: state.color,
			size: state.ratio > 1 ? state.ratio : dimensions.value.size * state.ratio
		} as AnimationParams
	})

	const startAnimation = (type : LoadingType) => {
		currentAnimation.value?.pause();
		if (currentAnimation.value?.type == type) {
			currentAnimation.value!.start()
			return
		}
		if (type == 'circular') {
			currentAnimation.value = createCircularAnimation(ctx.value!, animationParams.value)
			currentAnimation.value!.time = 1000 / 30
			currentAnimation.value!.start()
			return
		}
		if (type == 'spinner') {
			currentAnimation.value = createSpinnerAnimation(ctx.value!, animationParams.value)
			currentAnimation.value!.time = 1000 / 10
			currentAnimation.value!.start()
			return
		}
		if (type == 'failed') {
			currentAnimation.value = createFailedAnimation(ctx.value!, animationParams.value)
			currentAnimation.value?.start()
			return
		}
	}


	const failed = () => {
		startAnimation('failed')
	}
	const play = () => {
		startAnimation(state.type)
	}
	const clear = () => {
		currentAnimation.value?.stop();
		ctx.value?.reset();
		ctx.value?.update();
	}
	const destroy = () => {
		clear();
		resizeObserver.disconnect();
	}


	watch(animationParams, () => {
		if (['clear', 'destroy', 'pause'].includes(currentTick.value)) return
		startAnimation(state.type)
	})

	watchEffect(() => {
		if (ctx.value == null) return

		const tick = ticks.value.pop()
		if (ticks.value.length > 0) { }
		if (tick != null) {
			currentTick.value = tick
		}
		if (tick == 'play') {
			play()
			return
		}
		if (tick == 'failed') {
			failed()
			return
		}
		if (tick == 'clear') {
			clear()
			return
		}
		if (tick == 'destroy') {
			destroy()
			return
		}
		if (tick == 'pause') {
			if (currentAnimation.value == null) {
				play()
			}
			currentAnimation.value?.pause();
		}
	})

	watchEffect(() => {
		if (element.value == null) return
		resizeObserver.observe(element.value!);
		// #ifdef APP-IOS || APP-HARMONY
		// APP-HARMONY 在下拉组件中使用居中会影响绘制 故先获取一次
		updateDimensions(element.value!)
		// #endif
	})

	onUnmounted(destroy);


	return state
}