import { ref } from 'vue'
import hookFetch from 'hook-fetch'
import { sseTextDecoderPlugin } from 'hook-fetch/plugins/sse'
import type { ChatSseMessage } from '../types'

/**
 * 全局 HTTP 客户端实例
 * - timeout: 0 表示不设置超时，SSE 流式请求可能持续较长时间
 * - 挂载 sseTextDecoderPlugin 插件，将服务端返回的字节流解码为 SSE 文本事件
 */
const api = hookFetch.create({
	baseURL: '',
	timeout: 0,
	headers: {
		'Content-Type': 'application/json;charset=utf-8'
	}
})
api.use(sseTextDecoderPlugin({ json: false }))

/**
 * 聊天流式请求 Composable
 *
 * 封装 SSE（Server-Sent Events）流式对话的完整生命周期：
 * - 发起 POST 请求并持续读取响应流
 * - 解析 SSE 事件并逐块回调给调用方
 * - 处理正常完成、用户主动取消、网络异常三种终止场景
 *
 * @returns isLoading  - 是否正在加载中（响应式，可绑定 UI loading 状态）
 * @returns cancelRequest - 主动取消当前正在进行的流式请求
 * @returns streamChat   - 发起一次流式聊天请求
 */
export function useChatStream() {
	/** 当前是否有请求正在进行中，驱动 UI loading 状态 */
	const isLoading = ref(false)
	/** 当前活跃的 AbortController，用于中断正在进行的 HTTP 请求 */
	let activeController: AbortController | null = null
	/** 自增的流 ID，每次发起新请求时 +1，用于标识和区分不同的流 */
	let activeStreamId = 0
	/**
	 * 已主动取消的流 ID 集合
	 * 当用户取消请求或发起新请求导致旧流被取消时，将对应 streamId 存入此集合
	 * 用于在流结束后判断是"主动取消"还是"异常断连"，避免误报错误
	 */
	const cancelledStreamIds = new Set<number>()

	/**
	 * 取消当前正在进行的流式请求
	 * 将当前 streamId 记入取消集合，中断 HTTP 连接，重置 loading 状态
	 */
	const cancelRequest = () => {
		if (activeStreamId) {
			cancelledStreamIds.add(activeStreamId)
		}
		activeController?.abort()
		api.abortAll()
		isLoading.value = false
	}

	/**
	 * 解析单个 SSE 事件块为结构化消息数组
	 *
	 * SSE 协议中每条消息以 "data:" 前缀开头，多条消息之间用空行分隔。
	 * 本函数提取所有 data: 行的内容，尝试 JSON 反序列化；
	 * 若解析失败则降级为纯文本 answer 类型消息。
	 *
	 * @param block - 单个 SSE 事件文本块（可能包含多行 data:）
	 * @returns 解析后的消息数组
	 */
	const parseSseEvent = (block: string): ChatSseMessage[] => {
		// 提取所有 "data:" 前缀的行，去掉前缀后拼接
		const dataLines = block
			.split(/\r?\n/)
			.filter((line) => line.startsWith('data:'))
			.map((line) => line.replace(/^data:\s?/, ''))

		// 若无 data: 行，将整个块作为纯文本消息处理
		if (!dataLines.length) {
			const text = block.trim()
			return text ? [{ response_type: 'answer', content: text, done: false }] : []
		}

		const data = dataLines.join('\n').trim()

		console.log('msg:', data)
		try {
			// 尝试 JSON 解析为结构化消息
			return [JSON.parse(data)]
		} catch {
			// JSON 解析失败，降级为纯文本 answer 消息
			return [{ response_type: 'answer', content: data, done: false }]
		}
	}

	/**
	 * 解析一段完整的 SSE 数据块（可能包含多个事件）
	 *
	 * SSE 事件之间以双换行符（\n\n）分隔。
	 * 若块内包含多个事件，则拆分后逐个调用 parseSseEvent 解析。
	 *
	 * @param block - 从流中读取到的原始文本块
	 * @returns 所有解析后的消息数组
	 */
	const parseSseBlock = (block: string): ChatSseMessage[] => {
		// 以双换行符拆分，识别多个独立 SSE 事件
		const eventBlocks = block
			.split(/\r?\n\r?\n/)
			.map((item) => item.trim())
			.filter(Boolean)

		if (eventBlocks.length > 1) {
			return eventBlocks.flatMap(parseSseEvent)
		}

		return parseSseEvent(block)
	}

	/**
	 * 从错误响应对象中提取可读的错误信息
	 * 按优先级依次尝试 error / errors.message / message / msg 字段
	 *
	 * @param payload - 服务端返回的错误响应对象
	 * @returns 错误信息字符串，无匹配时返回空字符串
	 */
	const getErrorMessage = (payload: any) => {
		if (!payload || typeof payload !== 'object') return ''
		return `${payload.error || payload.errors?.message || payload.message || payload.msg || ''}`.trim()
	}

	/**
	 * 检测流式响应中的直接返回错误（非 SSE 格式的错误）
	 *
	 * 某些场景下服务端不返回 SSE 流，而是直接返回一个 JSON 错误对象
	 * （如鉴权失败、参数校验失败等）。本函数负责识别这类错误。
	 *
	 * 判断规则：
	 * - 响应不是标准 SSE 消息（无 response_type / data 字段）
	 * - 响应包含 isSuccess: false 标记
	 *
	 * @param result - 流中读取到的原始结果
	 * @returns 若为直接错误则返回 Error 对象，否则返回 null
	 */
	const normalizeDirectResponseError = (result: unknown) => {
		// 统一将字符串或对象类型的结果转为 payload 对象
		const payload =
			typeof result === 'string'
				? (() => {
						const text = result.trim()
						if (!text.startsWith('{') || !text.endsWith('}')) return null
						try {
							return JSON.parse(text)
						} catch {
							return null
						}
					})()
				: result && typeof result === 'object'
					? result
					: null

		if (!payload || Array.isArray(payload)) return null
		// 包含 SSE 特征字段，属于正常消息，不是直接错误
		if (payload.response_type || payload.data) return null
		// 业务层返回失败状态，构造错误对象
		if (payload.isSuccess === false) {
			return new Error(getErrorMessage(payload) || 'Request failed')
		}

		return null
	}

	/**
	 * 将各类请求错误统一标准化为 Error 对象
	 *
	 * @param error - 原始错误（可能是 Error 实例、HTTP 响应对象或其他类型）
	 * @returns 标准化后的 Error 对象
	 */
	const normalizeRequestError = async (error: any) => {
		if (!error) return new Error('Unknown error')
		if (error instanceof Error) return error

		const message =
			getErrorMessage(error) ||
			(error?.status ? `HTTP error! status: ${error.status}` : 'Request failed')

		return new Error(message)
	}

	const isTerminalEvent = (item: ChatSseMessage) => {
		const cmd = (item as ChatSseMessage & { cmd?: string }).cmd
		return (
			item?.response_type === 'complete' ||
			item?.response_type === 'error' ||
			cmd === 'CMD_AGENT_FINISH_MSG' ||
			cmd === 'CMD_AGENT_ERROR_MSG' ||
			cmd === 'CMD_CONNECT_ERROR_MSG' ||
			cmd === 'CMD_AGENT_SUSPEND_MSG'
		)
	}

	/**
	 * 发起一次 SSE 流式聊天请求
	 *
	 * 完整流程：
	 * 1. 取消上一次未完成的请求（确保同一时刻只有一个活跃流）
	 * 2. 构建带鉴权信息的 POST 请求
	 * 3. 通过 for-await 逐块读取响应流
	 * 4. 解析每个 SSE 事件并通过 onChunk 回调通知调用方
	 * 5. 检测到 complete/error 终止事件时结束流
	 * 6. 区分正常完成、用户取消、异常断连三种终止场景
	 *
	 * @param url       - 请求的 API 路径（如 /api/ai/chat/agent-chat）
	 * @param body      - 请求体对象，将被序列化为 JSON
	 * @param onChunk   - 每收到一个 SSE 事件时的回调，用于更新 UI 消息
	 * @param onComplete - 流正常结束时的回调
	 * @param onError   - 流异常终止时的回调
	 */
	const streamChat = async (
		url: string,
		body: any,
		onChunk: (chunk: ChatSseMessage) => void,
		onComplete: () => void,
		onError: (err: Error) => void
	) => {
		// 发起新请求前，取消上一次未完成的流（避免并发冲突）
		cancelRequest()

		isLoading.value = true
		// 分配新的流 ID，用于本次请求的唯一标识
		const streamId = activeStreamId + 1
		activeStreamId = streamId
		const controller = new AbortController()
		activeController = controller
		/** 是否已收到终止事件（complete 或 error） */
		let completeReceived = false
		/** 防止 onComplete 被重复调用的标记 */
		let completed = false
		/** 终端中止定时器（预留扩展，当前未使用） */
		let terminalAbortTimer: ReturnType<typeof setTimeout> | null = null

		/**
		 * 安全地触发完成回调，保证幂等（只调用一次）
		 */
		const finishStream = () => {
			if (!completed) {
				completed = true
				onComplete()
			}
		}

		try {
			// 从 Cookie 中读取会话鉴权 token（x-sessionId）
			const token = // localStorage.getItem('oauth2token') ||
				document.cookie.match(new RegExp('(^| )' + 'x-sessionId' + '=([^;]*)(;|$)'))?.[2]

			// 开发环境：请求代理到 VITE_BASE_URL 指定的后端地址
			// 生产环境：使用相对路径，由 Nginx 等反向代理转发
			const baseUrl = import.meta.env.DEV ? `http://${import.meta.env.VITE_BASE_URL}` : ''

			// 发起 POST 请求，开启流式响应
			const request = api.post(baseUrl + url, body, {
				signal: controller.signal,
				headers: {
					Authorization: token || ''
				},
				mode: 'cors',
				credentials: 'include'
			})

			// 逐块读取 SSE 响应流
			for await (const chunk of request.stream<string>()) {
				// 处理流式读取过程中出现的错误
				if (chunk.error) {
					throw await normalizeRequestError(chunk.error)
				}

				// 检测服务端直接返回的非 SSE 格式错误（如鉴权失败）
				const directResponseError = normalizeDirectResponseError(chunk.result)
				if (directResponseError) {
					throw directResponseError
				}

				// 将 chunk 结果转为字符串，跳过空内容
				const result = typeof chunk.result === 'string' ? chunk.result : `${chunk.result ?? ''}`
				if (!result.trim()) continue

				// 解析当前数据块中的所有 SSE 事件
				const results = parseSseBlock(result)
				// 逐条通知调用方处理
				results.forEach(onChunk)

				// 检测是否包含终止事件，兼容 response_type 与 runner cmd 两种协议。
				const hasTerminalEvent = results.some(isTerminalEvent)
				if (hasTerminalEvent) {
					completeReceived = true
					isLoading.value = false
					finishStream()
					// 终止 HTTP 连接，释放资源
					controller.abort()
					break
				}
			}

			// 流结束后：判断是否正常完成
			if (completeReceived) {
				// 已收到终止事件，确保 finishStream 被调用（幂等）
				finishStream()
			} else if (!cancelledStreamIds.has(streamId)) {
				// 未收到终止事件，且不是用户主动取消 → 异常断连，抛出错误
				throw new Error('Response stream ended before complete')
			}
			// 若 streamId 在 cancelledStreamIds 中，说明是主动取消，静默结束，不报错
		} catch (error: any) {
			if (error?.name === 'AbortError' && (completeReceived || cancelledStreamIds.has(streamId))) {
				// AbortError 来自正常的完成或主动取消，调用 finishStream 收尾
				finishStream()
			} else if (error?.name !== 'AbortError') {
				// 非 AbortError 的真实网络错误，通知调用方
				onError(await normalizeRequestError(error))
			}
		} finally {
			// 清理：无论成功或失败，都必须执行的收尾逻辑
			if (terminalAbortTimer) {
				clearTimeout(terminalAbortTimer)
			}
			// 仅当当前流仍是最新活跃流时才重置全局状态，避免覆盖新请求的状态
			if (activeStreamId === streamId) {
				activeController = null
				isLoading.value = false
			}
			// 从取消集合中移除当前流 ID，释放内存
			cancelledStreamIds.delete(streamId)
		}
	}

	return {
		isLoading,
		cancelRequest,
		streamChat
	}
}
