import { HttpError } from './http-error'
import { parseApiResponse, snapshotEnvelope } from './response-evaluator'

function finalizeResponse(res) {
	snapshotEnvelope(res)
	const parsed = parseApiResponse(res)
	if (parsed.kind === 'success' || parsed.kind === 'passthrough') {
		return {
			data: parsed.data,
			msg: parsed.message,
			raw: res
		}
	}
	throw HttpError.fromParsed(parsed)
}

export function request(options = {}) {
	return new Promise((resolve, reject) => {
		uni.request({
			...options,
			success(res) {
				try {
					resolve(finalizeResponse(res))
				} catch (err) {
					reject(err)
				}
			},
			fail(err) {
				reject(HttpError.fromNetwork(err))
			}
		})
	})
}

export function get(url, data, extra = {}) {
	return request({ url, data, method: 'GET', ...extra })
}

export function post(url, data, extra = {}) {
	return request({ url, data, method: 'POST', ...extra })
}

export function put(url, data, extra = {}) {
	return request({ url, data, method: 'PUT', ...extra })
}

export function del(url, data, extra = {}) {
	return request({ url, data, method: 'DELETE', ...extra })
}

export function upload(url, options = {}) {
	return new Promise((resolve, reject) => {
		uni.uploadFile({
			url,
			...options,
			success(res) {
				if (res.statusCode !== 200) {
					reject(HttpError.fromUpload(res))
					return
				}
				let data = res.data
				try {
					data = typeof data === 'string' ? JSON.parse(data) : data
				} catch (_) {}
				resolve({ data, raw: res })
			},
			fail(err) {
				reject(HttpError.fromNetwork(err))
			}
		})
	})
}

/**
 * 由路由表批量生成 API 方法
 * @example createApiModule({ profile: { path: buildRestPath('user/profile') } })
 */
export function createApiModule(routes) {
	const module = {}
	Object.keys(routes).forEach((name) => {
		const spec = routes[name]
		const method = (spec.method || 'GET').toUpperCase()
		const caller = method === 'GET' ? get : post
		module[name] = (data, extra) => caller(spec.path, data, { method, ...extra })
	})
	return module
}

export function createHttpFacade() {
	return { request, get, post, put, del, upload, createApiModule }
}
