/**
 * 通行证（passport）业务的 IO 桥接层。
 *
 * 与原生 uni.request / 老接口签名完全解耦，对页面层只暴露 5 个高内聚动词：
 *   - requestPasscode       触发短信下发
 *   - sessionByPasscode     使用手机号 + 短信码换取会话
 *   - sessionByCredential   使用账号 + 密码换取会话
 *   - enrollAccount         新建账号
 *   - verifyHandsetUsed     查询手机号是否已注册
 *
 * 全部接口的 dial 参数都接受"含/不含 + 号"两种写法，内部统一规范化。
 */

import { post } from '@/core/http/http-client'
import { HttpError } from '@/core/http/http-error'
import { buildRestPath } from '@/core/config/api-paths'
import { STORAGE } from '@/core/constants/storage-keys'

const ROUTE_SEND_PASSCODE = '/labsms/send'
const ROUTE_SECRET_LOGIN = buildRestPath('user/login')
const ROUTE_PASSCODE_LOGIN = '/user/mobilelogin'
const ROUTE_ENROLL = '/user/register'
const ROUTE_HANDSET_PROBE = '/user/checkIsRegister'

function readClientHandle() {
	return uni.getStorageSync(STORAGE.chatClient) || null
}

/**
 * 把页面层使用的"内部区号"统一规范成接口侧期望的"+区号"。
 */
export function normalizeRegion(value) {
	const raw = String(value || '').trim()
	if (!raw) return '+852'
	return raw.startsWith('+') ? raw : `+${raw}`
}

/**
 * 把不同后端响应形状归一为 store 期望的 `{ userinfo, statistics, ... }`。
 *
 * 兼容三类响应：
 *   A. 业务直接顶层 `{ userinfo, statistics }`（理想形态）
 *   B. 仍带 envelope `{ code:1, data: { userinfo, statistics } }`（项目拦截器拆包不彻底时）
 *   C. 扁平 `{ id, token, nickname, ... }`（少数老接口，没有 userinfo 嵌套）
 */
function readEnvelope(data, raw) {
	if (raw && raw.res && typeof raw.res === 'object') return raw.res
	if (raw && raw.data && typeof raw.data === 'object' && 'code' in raw.data) return raw.data
	if (data && typeof data === 'object' && 'code' in data) return data
	return (data && typeof data === 'object') ? data : {}
}

function assertBizOk(envelope, fallbackMsg = '请求失败') {
	const code = envelope && envelope.code != null ? Number(envelope.code) : 1
	if (code === 1) return
	if (code === 4) return
	const message = (envelope && envelope.msg) || fallbackMsg
	throw new HttpError('BUSINESS', message, envelope)
}

function unwrapSession(raw) {
	if (!raw || typeof raw !== 'object') return null
	if (raw.userinfo && typeof raw.userinfo === 'object') return raw
	if (raw.data && typeof raw.data === 'object' && raw.data.userinfo) return raw.data
	if (raw.token || raw.id) return { userinfo: raw }
	return raw
}

/**
 * @param {Object} payload
 * @param {string} payload.scene  下发事件 (mobilelogin / register / resetpwd ...)
 * @param {string} payload.handset 不含区号的纯数字
 * @param {string} payload.region 区号（含或不含 + 均可）
 * @returns {Promise<{ ticketId: string, needHumanCheck: boolean, raw: any }>}
 */
export async function requestPasscode({ scene, handset, region }) {
	const { data, raw } = await post(ROUTE_SEND_PASSCODE, {
		event: scene,
		mobile: handset,
		area_code: String(region || '').replace(/^\+/, '')
	})
	const envelope = readEnvelope(data, raw)
	const code = envelope.code != null ? Number(envelope.code) : 1
	if (code !== 1 && code !== 4) {
		assertBizOk(envelope)
	}
	return {
		ticketId: envelope.message_id || (data && data.message_id) || '',
		needHumanCheck: code === 4,
		raw: envelope
	}
}

/**
 * 手机号 + 短信验证码换取会话。
 */
export async function sessionByPasscode({ handset, region, passcode, ticketId }) {
	const { data } = await post(ROUTE_PASSCODE_LOGIN, {
		mobile: handset,
		area_code: normalizeRegion(region),
		message_id: ticketId || '',
		captcha: passcode,
		client_id: readClientHandle()
	})
	return unwrapSession(data)
}

/**
 * 账号（手机号）+ 密码换取会话。
 */
export async function sessionByCredential({ account, secret, ticketId }) {
	const { data } = await post(ROUTE_SECRET_LOGIN, {
		account,
		password: secret,
		message_id: ticketId || '',
		client_id: readClientHandle()
	})
	return unwrapSession(data)
}

/**
 * 创建账号。
 */
export async function enrollAccount({ handset, region, secret, passcode, ticketId }) {
	const { data } = await post(ROUTE_ENROLL, {
		mobile: handset,
		area_code: normalizeRegion(region),
		password: secret,
		code: passcode,
		message_id: ticketId || ''
	}, {
		header: { token: uni.getStorageSync(STORAGE.token) || '' }
	})
	return unwrapSession(data)
}

/**
 * 查询某号码是否已被注册。
 * @returns {Promise<{ taken: boolean, raw: any }>}
 */
export async function verifyHandsetUsed({ handset, region }) {
	const { data, raw } = await post(ROUTE_HANDSET_PROBE, {
		mobile: handset,
		area_code: normalizeRegion(region)
	})
	const envelope = (data && typeof data === 'object') ? data : (raw && raw.data) || {}
	const flag = envelope.is_exist != null
		? envelope.is_exist
		: (envelope.data && envelope.data.is_exist) || 0
	return {
		taken: Number(flag) === 1,
		raw: envelope
	}
}
