<template>
	<view class="checkout-page">
		<view v-if="loading" class="checkout-loading">
			<text class="ri-loader-4-line loading-spinner"></text>
			<text class="loading-text">正在加载订单...</text>
		</view>

		<view v-else-if="loadError" class="checkout-error">
			<text class="error-text">{{ loadError }}</text>
		</view>

		<view v-else class="checkout-sheet">
			<view v-if="returning" class="returning-mask">
				<text class="ri-loader-4-line loading-spinner"></text>
				<text class="returning-text">支付成功，正在返回...</text>
			</view>

			<view class="countdown-pill">
				<view class="countdown-pill-inner">
					<text class="countdown-label">支付剩余时间</text>
					<text class="countdown-value">{{ countdownText }}</text>
				</view>
			</view>

			<view class="amount-block">
				<text class="amount-currency">¥</text>
				<text class="amount-value">{{ amountText }}</text>
			</view>
			<view class="merchant-name">{{ subjectText }}</view>

			<view class="info-card">
				<view class="info-row">
					<text class="info-label">备注信息</text>
					<input
						v-model="remark"
						class="info-input"
						placeholder="点击填写订单备注"
						placeholder-class="info-placeholder"
						:disabled="paying || expired || returning"
					/>
				</view>
				<view class="info-row">
					<text class="info-label">订单信息</text>
					<text class="info-value">{{ bodyText }}</text>
				</view>
			</view>

			<view class="pay-method-card">
				<view class="pay-method-title">支付方式</view>
				<view class="pay-method-item">
					<view class="pay-method-left">
						<view class="wx-icon">
							<text class="ri-wechat-pay-fill"></text>
						</view>
						<text class="pay-method-name">微信支付</text>
					</view>
					<text class="ri-checkbox-circle-fill pay-method-check"></text>
				</view>
			</view>

			<button
				class="pay-button"
				hover-class="pay-button-hover"
				:loading="paying"
				:disabled="expired || returning || !tradeNo"
				@tap="handlePay"
			>
				确认支付 ¥{{ amountText }}
			</button>

			<view class="provider-footer">卓享汇收银台 提供服务</view>
		</view>
	</view>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import { onLoad, onUnload } from '@dcloudio/uni-app';
import { cancelTrade, cashTradeInfo, cashTradePay, queryTrade } from '@/api/cashier';
import { resolveLoginPlatform } from '@/api/auth';
import { isCheckoutClosing, returnToHostMiniProgram, resetCheckoutClosing } from '@/utils/checkout-return';
import { formatPaymentError, isPaymentCancelled, requestPlatformPayment } from '@/utils/wxpay';

const POLL_INTERVAL_MS = 1000;
const POLL_MAX_ATTEMPTS = 240;

const tradeNo = ref('');
const subjectText = ref('');
const bodyText = ref('');
const amountText = ref('0.00');
const remark = ref('');
const loading = ref(true);
const loadError = ref('');
const paying = ref(false);
const returning = ref(false);
const remainSeconds = ref(0);

let countdownTimer: ReturnType<typeof setInterval> | null = null;
let hasPayAttempted = false;
let paymentSucceeded = false;
let timeoutHandled = false;

const expired = computed(() => remainSeconds.value <= 0);
const countdownText = computed(() => {
	const minutes = String(Math.floor(remainSeconds.value / 60)).padStart(2, '0');
	const seconds = String(remainSeconds.value % 60).padStart(2, '0');
	return `${minutes}:${seconds}`;
});

const clearCountdown = () => {
	if (countdownTimer) {
		clearInterval(countdownTimer);
		countdownTimer = null;
	}
};

const parseDateTime = (value?: string) => {
	if (!value) return NaN;
	return new Date(value.replace(/-/g, '/')).getTime();
};

const resolveExpireAt = (trade: Record<string, any> = {}) => {
	const timeoutSeconds = Number(trade.timeout || 0);
	const createAt = parseDateTime(trade.create_time);
	if (Number.isFinite(createAt) && timeoutSeconds > 0) {
		return createAt + timeoutSeconds * 1000;
	}
	if (timeoutSeconds > 0) {
		return Date.now() + timeoutSeconds * 1000;
	}
	return Date.now() + 5 * 60 * 1000;
};

const startCountdown = (expireAt: number) => {
	clearCountdown();
	const tick = () => {
		const next = Math.max(0, Math.floor((expireAt - Date.now()) / 1000));
		remainSeconds.value = next;
		if (next <= 0) {
			clearCountdown();
			handleTimeout();
		}
	};
	tick();
	countdownTimer = setInterval(tick, 1000);
};

const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));

const isTradeNotPayable = (message = '') => {
	const text = String(message);
	return text.includes('订单状态') || text.includes('不允许付款') || text.includes('已取消') || text.includes('已关闭');
};

const finishCancelledAndReturn = async () => {
	if (isCheckoutClosing() || !tradeNo.value) return;

	try {
		await cancelTrade(tradeNo.value);
	} catch (error) {
		console.log('cancel trade failed', error);
	}

	await backToHost('cancelled', 0);
};

const pollTradePaid = async () => {
	for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {
		try {
			const response = await queryTrade(tradeNo.value);
			if (Number(response?.code) === 200 && Number(response?.data?.pay_status) === 1) {
				return true;
			}
		} catch (error) {
			console.log('trade query failed', error);
		}

		if (attempt < POLL_MAX_ATTEMPTS - 1) {
			await sleep(POLL_INTERVAL_MS);
		}
	}
	return false;
};

const backToHost = async (status: 'success' | 'cancelled' | 'timeout', payStatus?: number) => {
	if (isCheckoutClosing() || !tradeNo.value) return false;

	const returned = await returnToHostMiniProgram({
		status,
		trade_no: tradeNo.value,
		pay_status: payStatus
	});

	if (!returned) {
		resetCheckoutClosing();
	}

	return returned;
};

const handleTimeout = async () => {
	if (timeoutHandled || paymentSucceeded || isCheckoutClosing()) return;
	timeoutHandled = true;
	clearCountdown();

	try {
		await cancelTrade(tradeNo.value);
	} catch (error) {
		console.log('timeout cancel failed', error);
	}

	await backToHost('timeout', 0);
};

const loadOrderInfo = async () => {
	loading.value = true;
	loadError.value = '';

	try {
		const response = await cashTradeInfo(tradeNo.value);
		if (Number(response?.code) !== 200 || !response?.data) {
			throw new Error(response?.msg || '获取订单信息失败');
		}

		const payload = response.data || {};
		const trade = payload.trade || {};
		if (Number(trade.pay_status) === 1) {
			paymentSucceeded = true;
			await backToHost('success', 1);
			return;
		}

		subjectText.value = payload.subject || '';
		bodyText.value = payload.body || '';
		amountText.value = Number(trade.real_money || 0).toFixed(2);
		startCountdown(resolveExpireAt(trade));
	} catch (error: any) {
		loadError.value = error?.message || '获取订单信息失败';
	} finally {
		loading.value = false;
	}
};

const handlePay = async () => {
	if (paying.value || expired.value || returning.value || !tradeNo.value) return;

	paying.value = true;
	hasPayAttempted = true;

	try {
		const response = await cashTradePay({
			trade_no: tradeNo.value,
			platform: resolveLoginPlatform(),
			remark: remark.value.trim()
		});

		if (Number(response?.code) !== 200 || !response?.data) {
			const msg = response?.msg || '获取支付参数失败';
			if (isTradeNotPayable(msg)) {
				await finishCancelledAndReturn();
				return;
			}
			throw new Error(msg);
		}

		const payload = response.data || {};

		try {
			await requestPlatformPayment(payload);
		} catch (error) {
			if (isPaymentCancelled(error)) {
				await finishCancelledAndReturn();
				return;
			}
			throw error;
		}

		returning.value = true;
		const paid = await pollTradePaid();
		if (paid) {
			paymentSucceeded = true;
			await backToHost('success', 1);
			return;
		}

		await backToHost('success', 0);
	} catch (error: any) {
		const message = formatPaymentError(error);
		if (isTradeNotPayable(message)) {
			await finishCancelledAndReturn();
			return;
		}
		uni.showToast({
			title: message,
			icon: 'none'
		});
	} finally {
		paying.value = false;
		returning.value = false;
	}
};

const decodeQuery = (value?: string) => {
	if (!value) return '';
	try {
		return decodeURIComponent(value);
	} catch (error) {
		return value;
	}
};

onLoad((options: any) => {
	const nextTradeNo = decodeQuery(options?.trade_no || options?.tradeNo).trim();
	if (!nextTradeNo) {
		loadError.value = '缺少交易单号';
		loading.value = false;
		return;
	}

	tradeNo.value = nextTradeNo;
	loadOrderInfo();
});

onUnload(() => {
	clearCountdown();

	if (isCheckoutClosing() || paymentSucceeded || !tradeNo.value) return;

	const currentTradeNo = tradeNo.value;
	cancelTrade(currentTradeNo).catch(() => {});
	returnToHostMiniProgram({
		status: 'cancelled',
		trade_no: currentTradeNo,
		pay_status: 0
	});
});
</script>

<style lang="scss" scoped>
.checkout-page {
	box-sizing: border-box;
	min-height: 100vh;
	padding: 24rpx 0 calc(32rpx + env(safe-area-inset-bottom));
	background: #f3f4f6;
}

.checkout-loading,
.checkout-error {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	min-height: 60vh;
	gap: 16rpx;
}

.loading-spinner {
	color: #1f5bd8;
	font-size: 56rpx;
	animation: spin 1s linear infinite;
}

.loading-text,
.error-text,
.returning-text {
	color: #6b7280;
	font-size: 28rpx;
}

.error-text {
	color: #ef4444;
}

.checkout-sheet {
	position: relative;
	padding: 16rpx 32rpx 0;
}

.returning-mask {
	position: absolute;
	inset: 0;
	z-index: 20;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 16rpx;
	background: rgba(255, 255, 255, 0.92);
}

@keyframes spin {
	from {
		transform: rotate(0deg);
	}
	to {
		transform: rotate(360deg);
	}
}

.countdown-pill {
	display: flex;
	justify-content: center;
	margin-bottom: 40rpx;
}

.countdown-pill-inner {
	display: inline-flex;
	align-items: center;
	gap: 12rpx;
	padding: 10rpx 24rpx;
	border-radius: 999rpx;
	background: #ffffff;
	box-shadow: 0 4rpx 16rpx rgba(17, 24, 39, 0.06);
}

.countdown-label,
.countdown-value {
	color: #6b7280;
	font-size: 24rpx;
	line-height: 1.2;
}

.countdown-value {
	color: #111827;
	font-weight: 700;
}

.amount-block {
	display: flex;
	align-items: baseline;
	justify-content: center;
}

.amount-currency {
	margin-right: 8rpx;
	color: #111827;
	font-size: 44rpx;
	font-weight: 700;
	line-height: 1;
}

.amount-value {
	color: #111827;
	font-size: 72rpx;
	font-weight: 800;
	line-height: 1;
}

.merchant-name {
	margin-top: 20rpx;
	text-align: center;
	color: #6b7280;
	font-size: 28rpx;
}

.info-card,
.pay-method-card {
	margin-top: 40rpx;
	border-radius: 16rpx;
	background: #ffffff;
}

.info-card {
	padding: 4rpx 0;
}

.info-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 24rpx;
	padding: 28rpx;
	border-bottom: 1rpx solid #f0f2f5;
}

.info-row:last-child {
	border-bottom: none;
}

.info-label {
	flex-shrink: 0;
	color: #6b7280;
	font-size: 28rpx;
}

.info-input,
.info-value {
	flex: 1;
	color: #111827;
	font-size: 28rpx;
	text-align: right;
}

.info-placeholder {
	color: #c2c7d0;
}

.pay-method-card {
	padding: 28rpx;
}

.pay-method-title {
	margin-bottom: 20rpx;
	color: #6b7280;
	font-size: 28rpx;
}

.pay-method-item {
	display: flex;
	align-items: center;
	justify-content: space-between;
}

.pay-method-left {
	display: flex;
	align-items: center;
	gap: 16rpx;
}

.wx-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 48rpx;
	height: 48rpx;
	border-radius: 50%;
	background: #07c160;
	color: #ffffff;
	font-size: 36rpx;
	line-height: 1;
}

.pay-method-name {
	color: #111827;
	font-size: 30rpx;
	font-weight: 600;
}

.pay-method-check {
	color: #07c160;
	font-size: 36rpx;
	line-height: 1;
}

.pay-button {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 92rpx;
	margin-top: 48rpx;
	padding: 0;
	border-radius: 12rpx;
	background: #1f5bd8;
	color: #ffffff;
	font-size: 32rpx;
	font-weight: 700;
}

.pay-button::after {
	border: none;
}

.pay-button-hover {
	background: #174dc1;
}

.pay-button[disabled] {
	background: #c8d3ee;
	color: #ffffff;
}

.provider-footer {
	margin-top: 28rpx;
	text-align: center;
	color: #9ca3af;
	font-size: 24rpx;
}
</style>
