import { useCallback, useEffect, useMemo, useState } from "react";
import {
    approveFinanceWithdrawalApplication,
    approveOperationsWithdrawalApplication,
    createFinanceCoinSellerRechargeOrder,
    exportFinanceRechargeBills,
    fetchFinanceOptions,
    fetchFinanceRechargeApps,
    fetchFinanceRechargeOverview,
    fetchFinanceRechargeSummaries,
    fetchFinanceSession,
    fetchFinanceWithdrawalOptions,
    grantFinanceCoinSellerRechargeOrder,
    getFinanceCoinSellerRechargeExchangeRate,
    getFinanceUSDTAddresses,
    listFinanceCoinSellerRechargeOrders,
    listFinanceRechargeDetails,
    listFinanceRechargeRegions,
    listFinanceWithdrawalApplications,
    listOperationsWithdrawalApplications,
    refreshFinanceGoogleRechargePaid,
    replaceFinanceCoinSellerRechargeExchangeRate,
    rejectFinanceWithdrawalApplication,
    rejectOperationsWithdrawalApplication,
    quoteFinanceCoinSellerRechargeOrder,
    upsertFinanceUSDTAddress,
    verifyFinanceCoinSellerRechargeReceipt,
    verifyFinanceCoinSellerRechargeOrder,
} from "./api.js";
import { FinanceAuditDialog } from "./components/FinanceAuditDialog.jsx";
import { FinanceCoinSellerRechargeOrderList } from "./components/FinanceCoinSellerRechargeOrderList.jsx";
import { FinanceOverview } from "./components/FinanceOverview.jsx";
import { FinanceReconciliation } from "./components/FinanceReconciliation.jsx";
import { FinanceRechargeDetailList } from "./components/FinanceRechargeDetailList.jsx";
import { FinanceShell } from "./components/FinanceShell.jsx";
import { FinanceWithdrawalApplicationList } from "./components/FinanceWithdrawalApplicationList.jsx";
import { EMPTY_RECHARGE_OVERVIEW, EMPTY_RECHARGE_SUMMARY } from "./format.js";
import { downloadCsv } from "@/shared/api/download";
import { buildLoginRedirectPath } from "@/features/auth/loginRedirect.js";
import { AppIdentity } from "@/shared/ui/AppIdentity.jsx";
import { TimeRangeFilter } from "@/shared/ui/TimeRangeFilter.jsx";
import { useToast } from "@/shared/ui/ToastProvider.jsx";

const emptyOptions = { apps: [] };
const pageSize = 50;
const DAY_MS = 86_400_000;
const CHINA_OFFSET_MS = 8 * 3_600_000;
const RECHARGE_VIEWS = new Set(["overview", "rechargeDetails", "recon"]);
const FINANCE_VIEW_ALIASES = new Map([["withdrawals", "withdrawalFinance"]]);

function requestedFinanceView() {
    const requested = new URLSearchParams(window.location.search).get("view") || "";
    return FINANCE_VIEW_ALIASES.get(requested) || requested;
}

function canAccessFinanceView(session, view) {
    if (RECHARGE_VIEWS.has(view)) {
        return Boolean(session?.canViewAppRechargeDetails);
    }
    if (view === "coinSellerRechargeOrders") {
        return Boolean(session?.canViewCoinSellerRechargeOrders);
    }
    if (view === "withdrawalOperations") {
        return Boolean(session?.canViewOperationsWithdrawalApplications);
    }
    if (view === "withdrawalFinance") {
        return Boolean(session?.canViewWithdrawalApplications);
    }
    return false;
}

function defaultFinanceView(session) {
    if (session?.canViewAppRechargeDetails) {
        return "overview";
    }
    if (session?.canViewCoinSellerRechargeOrders) {
        return "coinSellerRechargeOrders";
    }
    if (session?.canViewOperationsWithdrawalApplications) {
        return "withdrawalOperations";
    }
    if (session?.canViewWithdrawalApplications) {
        return "withdrawalFinance";
    }
    return "";
}

function resolvedFinanceView(session) {
    const requested = requestedFinanceView();
    return canAccessFinanceView(session, requested) ? requested : defaultFinanceView(session);
}

function syncFinanceView(view) {
    if (!view) {
        return;
    }
    const url = new URL(window.location.href);
    if (url.searchParams.get("view") === view) {
        return;
    }
    // 工作台是独立 HTML 入口，内部视图只同步 query；保留其他筛选参数和 hash，避免点击菜单破坏外部深链上下文。
    url.searchParams.set("view", view);
    window.history.replaceState(window.history.state, "", `${url.pathname}${url.search}${url.hash}`);
}

function chinaDayStart(timestamp) {
    return Math.floor((timestamp + CHINA_OFFSET_MS) / DAY_MS) * DAY_MS - CHINA_OFFSET_MS;
}

function timePresets() {
    const now = Date.now();
    const todayStart = chinaDayStart(now);
    const yesterdayStart = todayStart - DAY_MS;
    const chinaNow = new Date(now + CHINA_OFFSET_MS);
    const monthStart = Date.UTC(chinaNow.getUTCFullYear(), chinaNow.getUTCMonth(), 1) - CHINA_OFFSET_MS;
    return [
        { key: "today", label: "今天", range: { endMs: todayStart + DAY_MS, startMs: todayStart } },
        // 财务后台统计按中国自然日切分，昨天固定为昨日 00:00 到今日 00:00，避免滚动 24 小时影响对账口径。
        { key: "yesterday", label: "昨天", range: { endMs: todayStart, startMs: yesterdayStart } },
        { key: "7d", label: "近7天", range: { endMs: todayStart + DAY_MS, startMs: todayStart - 6 * DAY_MS } },
        { key: "30d", label: "近30天", range: { endMs: todayStart + DAY_MS, startMs: todayStart - 29 * DAY_MS } },
        { key: "month", label: "本月", range: { endMs: todayStart + DAY_MS, startMs: monthStart } },
        { key: "all", label: "不限", range: { endMs: "", startMs: "" } },
    ];
}

function reconMonthMeta(offset) {
    const chinaNow = new Date(Date.now() + CHINA_OFFSET_MS);
    const year = chinaNow.getUTCFullYear();
    const month = chinaNow.getUTCMonth() + offset;
    const startMs = Date.UTC(year, month, 1) - CHINA_OFFSET_MS;
    const endMs = Date.UTC(year, month + 1, 1) - CHINA_OFFSET_MS;
    const startDate = new Date(startMs + CHINA_OFFSET_MS);
    return {
        endMs,
        isCurrent: offset === 0,
        label: `${startDate.getUTCFullYear()}年${startDate.getUTCMonth() + 1}月`,
        startMs,
    };
}

export function FinanceApp() {
    const { showToast } = useToast();
    const [sessionState, setSessionState] = useState({ error: "", loading: true, session: null });
    const [optionsState, setOptionsState] = useState({ data: emptyOptions, error: "", loading: false });
    const [withdrawalOptionsState, setWithdrawalOptionsState] = useState({
        data: emptyOptions,
        error: "",
        loading: false,
    });
    const [rechargeDetailsState, setRechargeDetailsState] = useState({
        data: { items: [], page: 1, pageSize, total: 0 },
        error: "",
        loading: false,
    });
    const [rechargeSummaryState, setRechargeSummaryState] = useState({
        merged: EMPTY_RECHARGE_SUMMARY,
        loading: false,
        perApp: [],
        prev: null,
    });
    const [rechargeOverviewState, setRechargeOverviewState] = useState({
        data: EMPTY_RECHARGE_OVERVIEW,
        loading: false,
    });
    const [rechargeRegionsState, setRechargeRegionsState] = useState({ byApp: {}, loading: false });
    const [rechargeAppsState, setRechargeAppsState] = useState({ items: [], loaded: false });
    const [googlePaidRefreshing, setGooglePaidRefreshing] = useState(false);
    const [reconMonthOffset, setReconMonthOffset] = useState(0);
    const [reconState, setReconState] = useState({
        loading: false,
        overview: EMPTY_RECHARGE_OVERVIEW,
        summary: EMPTY_RECHARGE_SUMMARY,
    });
    const [withdrawalsState, setWithdrawalsState] = useState({
        data: { items: [], page: 1, pageSize, total: 0 },
        error: "",
        loading: false,
    });
    const [operationsWithdrawalsState, setOperationsWithdrawalsState] = useState({
        data: { items: [], page: 1, pageSize, total: 0 },
        error: "",
        loading: false,
    });
    const [coinSellerRechargeOrdersState, setCoinSellerRechargeOrdersState] = useState({
        data: { items: [], page: 1, pageSize, total: 0 },
        error: "",
        loading: false,
    });
    const [coinSellerRechargeOrderActionLoading, setCoinSellerRechargeOrderActionLoading] = useState("");
    const [activeView, setActiveView] = useState("overview");
    const [rechargeDetailFilters, setRechargeDetailFilters] = useState(() => ({
        appCode: "",
        currency: "usd",
        keyword: "",
        page: 1,
        paidState: "",
        rechargeType: "",
        regionId: "",
        timeRange: timePresets().find((preset) => preset.key === "yesterday").range,
    }));
    const [rechargeExporting, setRechargeExporting] = useState(false);
    const [withdrawalFilters, setWithdrawalFilters] = useState({ appCode: "", keyword: "", page: 1 });
    const [operationsWithdrawalFilters, setOperationsWithdrawalFilters] = useState({
        appCode: "",
        keyword: "",
        page: 1,
    });
    const [coinSellerRechargeOrderFilters, setCoinSellerRechargeOrderFilters] = useState({
        appCode: "",
        grantStatus: "",
        keyword: "",
        page: 1,
        providerCode: "",
        status: "",
        verifyStatus: "",
    });
    const [withdrawalAuditTarget, setWithdrawalAuditTarget] = useState(null);
    const [withdrawalAuditLoading, setWithdrawalAuditLoading] = useState(false);

    useEffect(() => {
        let mounted = true;
        fetchFinanceSession()
            .then((session) => {
                if (!mounted) {
                    return;
                }
                setSessionState({ error: "", loading: false, session });
                const nextView = resolvedFinanceView(session);
                setActiveView(nextView);
                syncFinanceView(nextView);
            })
            .catch((err) => {
                if (mounted) {
                    setSessionState({ error: err.message || "登录已失效", loading: false, session: null });
                }
            });
        return () => {
            mounted = false;
        };
    }, []);

    const session = sessionState.session;
    const canLoadOptions = Boolean(session?.canAccessFinance);
    const canLoadWithdrawalOptions = Boolean(
        session?.canViewOperationsWithdrawalApplications || session?.canViewWithdrawalApplications,
    );
    const canViewFinance = Boolean(session?.canViewAppRechargeDetails);

    // 财务三页（概览/流水/对账）共享同一套 App × 时间 × 计价 全局筛选。
    const rechargeBaseQuery = useMemo(
        () => ({
            app_code: rechargeDetailFilters.appCode,
            end_at_ms: rechargeDetailFilters.timeRange.endMs,
            keyword: rechargeDetailFilters.keyword,
            recharge_type: rechargeDetailFilters.rechargeType,
            region_id: rechargeDetailFilters.appCode ? rechargeDetailFilters.regionId : "",
            start_at_ms: rechargeDetailFilters.timeRange.startMs,
            stat_tz: "Asia/Shanghai",
            status: "succeeded",
        }),
        [rechargeDetailFilters],
    );
    const rechargeDetailQuery = useMemo(
        () => ({
            ...rechargeBaseQuery,
            page: rechargeDetailFilters.page,
            page_size: pageSize,
            paid_state: rechargeDetailFilters.paidState,
        }),
        [rechargeBaseQuery, rechargeDetailFilters.page, rechargeDetailFilters.paidState],
    );
    const withdrawalQuery = useMemo(
        () => ({
            app_code: withdrawalFilters.appCode,
            keyword: withdrawalFilters.keyword,
            page: withdrawalFilters.page,
            page_size: pageSize,
        }),
        [withdrawalFilters],
    );
    const operationsWithdrawalQuery = useMemo(
        () => ({
            app_code: operationsWithdrawalFilters.appCode,
            keyword: operationsWithdrawalFilters.keyword,
            page: operationsWithdrawalFilters.page,
            page_size: pageSize,
        }),
        [operationsWithdrawalFilters],
    );
    const coinSellerRechargeOrderQuery = useMemo(
        () => ({
            app_code: coinSellerRechargeOrderFilters.appCode,
            grant_status: coinSellerRechargeOrderFilters.grantStatus,
            keyword: coinSellerRechargeOrderFilters.keyword,
            page: coinSellerRechargeOrderFilters.page,
            page_size: pageSize,
            provider_code: coinSellerRechargeOrderFilters.providerCode,
            status: coinSellerRechargeOrderFilters.status,
            verify_status: coinSellerRechargeOrderFilters.verifyStatus,
        }),
        [coinSellerRechargeOrderFilters],
    );

    const loadOptions = useCallback(async () => {
        if (!canLoadOptions) {
            return;
        }
        setOptionsState((current) => ({ ...current, error: "", loading: true }));
        try {
            const data = await fetchFinanceOptions();
            setOptionsState({ data, error: "", loading: false });
        } catch (err) {
            setOptionsState({ data: emptyOptions, error: err.message || "加载财务选项失败", loading: false });
        }
    }, [canLoadOptions]);

    const loadWithdrawalOptions = useCallback(async () => {
        if (!canLoadWithdrawalOptions) {
            return;
        }
        setWithdrawalOptionsState((current) => ({ ...current, error: "", loading: true }));
        try {
            const data = await fetchFinanceWithdrawalOptions();
            setWithdrawalOptionsState({ data, error: "", loading: false });
        } catch (err) {
            // 财务范围加载失败时必须保持空目录，不能回退全量 options 造成越权筛选入口。
            setWithdrawalOptionsState({
                data: emptyOptions,
                error: err.message || "加载提现 App 范围失败",
                loading: false,
            });
        }
    }, [canLoadWithdrawalOptions]);

    // 充值详情的 App 目录来自 recharge-apps（含 Yumi/Aslan 等 legacy 外部账单源）；接口未就绪时回退到财务通用 App 列表。
    const rechargeApps = useMemo(
        () =>
            rechargeAppsState.loaded && rechargeAppsState.items.length
                ? rechargeAppsState.items
                : optionsState.data.apps,
        [optionsState.data.apps, rechargeAppsState],
    );

    const loadRechargeApps = useCallback(async () => {
        if (!canViewFinance) {
            return;
        }
        try {
            const items = await fetchFinanceRechargeApps();
            setRechargeAppsState({ items, loaded: true });
        } catch {
            setRechargeAppsState({ items: [], loaded: false });
        }
    }, [canViewFinance]);

    const loadRechargeDetails = useCallback(async () => {
        if (!canViewFinance) {
            return;
        }
        setRechargeDetailsState((current) => ({ ...current, error: "", loading: true }));
        try {
            const data = await listFinanceRechargeDetails(rechargeDetailQuery, rechargeApps);
            setRechargeDetailsState({ data, error: "", loading: false });
        } catch (err) {
            setRechargeDetailsState((current) => ({
                ...current,
                error: err.message || "加载充值流水失败",
                loading: false,
            }));
        }
    }, [canViewFinance, rechargeApps, rechargeDetailQuery]);

    const loadRechargeSummary = useCallback(async () => {
        if (!canViewFinance) {
            return;
        }
        setRechargeSummaryState((current) => ({ ...current, loading: true }));
        try {
            const { merged, perApp } = await fetchFinanceRechargeSummaries(rechargeBaseQuery, rechargeApps);
            // 环比：把当前时间范围整体前移一个周期再查一次；未选时间范围时不算环比。
            let prev = null;
            const { endMs, startMs } = rechargeDetailFilters.timeRange;
            if (startMs && endMs && endMs > startMs) {
                const length = endMs - startMs;
                const prevQuery = { ...rechargeBaseQuery, end_at_ms: startMs, start_at_ms: startMs - length };
                prev = (await fetchFinanceRechargeSummaries(prevQuery, rechargeApps)).merged;
            }
            setRechargeSummaryState({ merged, loading: false, perApp, prev });
        } catch {
            setRechargeSummaryState((current) => ({ ...current, loading: false }));
        }
    }, [canViewFinance, rechargeApps, rechargeBaseQuery, rechargeDetailFilters.timeRange]);

    const loadRechargeOverview = useCallback(async () => {
        if (!canViewFinance) {
            return;
        }
        setRechargeOverviewState((current) => ({ ...current, loading: true }));
        try {
            const data = await fetchFinanceRechargeOverview(rechargeBaseQuery, rechargeApps);
            setRechargeOverviewState({ data, loading: false });
        } catch {
            setRechargeOverviewState((current) => ({ ...current, loading: false }));
        }
    }, [canViewFinance, rechargeApps, rechargeBaseQuery]);

    const loadRecon = useCallback(async () => {
        if (!canViewFinance || activeView !== "recon") {
            return;
        }
        const month = reconMonthMeta(reconMonthOffset);
        const query = {
            ...rechargeBaseQuery,
            end_at_ms: month.endMs,
            keyword: "",
            recharge_type: "",
            start_at_ms: month.startMs,
        };
        setReconState((current) => ({ ...current, loading: true }));
        try {
            const [{ merged }, overview] = await Promise.all([
                fetchFinanceRechargeSummaries(query, rechargeApps),
                fetchFinanceRechargeOverview(query, rechargeApps),
            ]);
            setReconState({ loading: false, overview, summary: merged });
        } catch {
            setReconState((current) => ({ ...current, loading: false }));
        }
    }, [activeView, canViewFinance, rechargeApps, rechargeBaseQuery, reconMonthOffset]);

    const rechargeAppCode = rechargeDetailFilters.appCode;
    // 一次拉齐所有 App 的区域目录：区域筛选下拉用当前 App 的，流水区域列在“全部 App”下要跨 App 显示名称。
    const loadRechargeRegions = useCallback(async () => {
        if (!canViewFinance || !rechargeApps.length) {
            return;
        }
        setRechargeRegionsState((current) => ({ ...current, loading: true }));
        try {
            const entries = await Promise.all(
                rechargeApps.map(async (app) => {
                    try {
                        return [app.appCode, await listFinanceRechargeRegions(app.appCode)];
                    } catch {
                        return [app.appCode, []];
                    }
                }),
            );
            setRechargeRegionsState({ byApp: Object.fromEntries(entries), loading: false });
        } catch {
            setRechargeRegionsState((current) => ({ ...current, loading: false }));
        }
    }, [canViewFinance, rechargeApps]);

    const refreshGooglePaid = useCallback(
        async (transactionIds) => {
            if (!rechargeAppCode || !transactionIds?.length || googlePaidRefreshing) {
                return;
            }
            setGooglePaidRefreshing(true);
            try {
                const results = await refreshFinanceGoogleRechargePaid(rechargeAppCode, transactionIds);
                const failed = results.filter((item) => item.error).length;
                const succeeded = results.length - failed;
                showToast(
                    failed
                        ? `谷歌实付同步完成：成功 ${succeeded} 笔，失败 ${failed} 笔`
                        : `谷歌实付同步完成：${succeeded} 笔`,
                    failed ? "warning" : "success",
                );
                await Promise.all([loadRechargeDetails(), loadRechargeOverview()]);
            } catch (err) {
                showToast(err.message || "查询谷歌实付失败", "error");
            } finally {
                setGooglePaidRefreshing(false);
            }
        },
        [googlePaidRefreshing, loadRechargeDetails, loadRechargeOverview, rechargeAppCode, showToast],
    );

    const exportRechargeBills = useCallback(async () => {
        if (!rechargeAppCode || rechargeExporting) {
            return;
        }
        setRechargeExporting(true);
        try {
            const timestamp = new Date()
                .toLocaleString("sv-SE", { timeZone: "Asia/Shanghai" })
                .replaceAll(/[: ]/g, "-");
            await downloadCsv(
                () => exportFinanceRechargeBills(rechargeAppCode, rechargeDetailQuery),
                `充值明细-${rechargeAppCode}-${timestamp}.csv`,
            );
            showToast("充值明细已导出", "success");
        } catch (err) {
            showToast(err.message || "导出充值明细失败", "error");
        } finally {
            setRechargeExporting(false);
        }
    }, [rechargeAppCode, rechargeDetailQuery, rechargeExporting, showToast]);

    const loadWithdrawals = useCallback(async () => {
        if (!session?.canViewWithdrawalApplications) {
            return;
        }
        setWithdrawalsState((current) => ({ ...current, error: "", loading: true }));
        try {
            const data = await listFinanceWithdrawalApplications(withdrawalQuery);
            setWithdrawalsState({ data, error: "", loading: false });
        } catch (err) {
            setWithdrawalsState((current) => ({
                ...current,
                error: err.message || "加载提现申请列表失败",
                loading: false,
            }));
        }
    }, [session?.canViewWithdrawalApplications, withdrawalQuery]);

    const loadOperationsWithdrawals = useCallback(async () => {
        if (!session?.canViewOperationsWithdrawalApplications) {
            return;
        }
        setOperationsWithdrawalsState((current) => ({ ...current, error: "", loading: true }));
        try {
            const data = await listOperationsWithdrawalApplications(operationsWithdrawalQuery);
            setOperationsWithdrawalsState({ data, error: "", loading: false });
        } catch (err) {
            setOperationsWithdrawalsState((current) => ({
                ...current,
                error: err.message || "加载用户提现运营审核列表失败",
                loading: false,
            }));
        }
    }, [operationsWithdrawalQuery, session?.canViewOperationsWithdrawalApplications]);

    const loadCoinSellerRechargeOrders = useCallback(async () => {
        if (!session?.canViewCoinSellerRechargeOrders) {
            return;
        }
        setCoinSellerRechargeOrdersState((current) => ({ ...current, error: "", loading: true }));
        try {
            const data = await listFinanceCoinSellerRechargeOrders(coinSellerRechargeOrderQuery);
            setCoinSellerRechargeOrdersState({ data, error: "", loading: false });
        } catch (err) {
            setCoinSellerRechargeOrdersState((current) => ({
                ...current,
                error: err.message || "加载币商充值订单失败",
                loading: false,
            }));
        }
    }, [coinSellerRechargeOrderQuery, session?.canViewCoinSellerRechargeOrders]);

    useEffect(() => {
        loadOptions();
    }, [loadOptions]);

    useEffect(() => {
        loadWithdrawalOptions();
    }, [loadWithdrawalOptions]);

    useEffect(() => {
        loadRechargeApps();
    }, [loadRechargeApps]);

    useEffect(() => {
        loadRechargeDetails();
    }, [loadRechargeDetails]);

    useEffect(() => {
        loadRechargeSummary();
    }, [loadRechargeSummary]);

    useEffect(() => {
        loadRechargeOverview();
    }, [loadRechargeOverview]);

    useEffect(() => {
        loadRecon();
    }, [loadRecon]);

    useEffect(() => {
        loadRechargeRegions();
    }, [loadRechargeRegions]);

    useEffect(() => {
        loadWithdrawals();
    }, [loadWithdrawals]);

    useEffect(() => {
        loadOperationsWithdrawals();
    }, [loadOperationsWithdrawals]);

    useEffect(() => {
        loadCoinSellerRechargeOrders();
    }, [loadCoinSellerRechargeOrders]);

    const submitWithdrawalAudit = async ({ auditImageUrl, decision, remark }) => {
        const reviewStage = withdrawalAuditTarget?.reviewStage === "operations" ? "operations" : "finance";
        const canAudit =
            reviewStage === "operations"
                ? session?.canAuditOperationsWithdrawalApplications
                : session?.canAuditWithdrawalApplications;
        const isOperationsRejectRetry =
            reviewStage === "operations" && withdrawalAuditTarget?.operationsStatus === "rejecting";
        // rejecting 已锁定为拒绝方向，前端门禁不得再把该记录提交到通过接口。
        if (!withdrawalAuditTarget?.id || !canAudit || (isOperationsRejectRetry && decision !== "rejected")) {
            return;
        }
        setWithdrawalAuditLoading(true);
        try {
            const submitter =
                reviewStage === "operations"
                    ? decision === "approved"
                        ? approveOperationsWithdrawalApplication
                        : rejectOperationsWithdrawalApplication
                    : decision === "approved"
                      ? approveFinanceWithdrawalApplication
                      : rejectFinanceWithdrawalApplication;
            const payload =
                reviewStage === "operations"
                    ? cleanPayload({ auditRemark: remark })
                    : cleanPayload({ auditImageUrl, auditRemark: remark });
            await submitter(withdrawalAuditTarget.id, withdrawalAuditTarget.appCode, payload);
            const stageLabel = reviewStage === "operations" ? "运营审核" : "财务审核";
            showToast(`${stageLabel}${decision === "approved" ? "已通过" : "已拒绝"}`, "success");
            setWithdrawalAuditTarget(null);
            // 平台管理员可能同时打开两个队列；任一阶段落库后刷新两边，运营通过即可立即出现在财务队列。
            await Promise.all([loadOperationsWithdrawals(), loadWithdrawals()]);
        } catch (err) {
            showToast(err.message || "审核用户提现失败", "error");
        } finally {
            setWithdrawalAuditLoading(false);
        }
    };

    const submitCoinSellerRechargeOrder = async (payload) => {
        if (!session?.canCreateCoinSellerRechargeOrder) {
            showToast("无创建币商充值订单权限", "error");
            return null;
        }
        setCoinSellerRechargeOrderActionLoading("create");
        try {
            const order = await createFinanceCoinSellerRechargeOrder(cleanPayload(payload));
            showToast("币商充值订单已创建", "success");
            await loadCoinSellerRechargeOrders();
            return order;
        } catch (err) {
            showToast(err.message || "创建币商充值订单失败", "error");
            return null;
        } finally {
            setCoinSellerRechargeOrderActionLoading("");
        }
    };

    const quoteCoinSellerRechargeOrder = useCallback(
        (payload) => quoteFinanceCoinSellerRechargeOrder(cleanPayload(payload)),
        [],
    );

    const loadCoinSellerRechargeExchangeRate = useCallback(
        (appCode) => getFinanceCoinSellerRechargeExchangeRate(appCode),
        [],
    );

    const loadUSDTAddresses = useCallback((appCode) => getFinanceUSDTAddresses(appCode), []);

    const saveUSDTAddress = useCallback(
        async (appCode, chain, payload) => {
            if (!session?.canUpdateUSDTAddress) {
                throw new Error("无修改 USDT 收款地址权限");
            }
            const saved = await upsertFinanceUSDTAddress(appCode, chain, cleanPayload(payload));
            showToast(`${chain} 收款地址已保存`, "success");
            return saved;
        },
        [session?.canUpdateUSDTAddress, showToast],
    );

    const saveCoinSellerRechargeExchangeRate = useCallback(
        async (appCode, payload) => {
            const saved = await replaceFinanceCoinSellerRechargeExchangeRate(appCode, cleanPayload(payload));
            showToast("金币汇率配置已保存", "success");
            return saved;
        },
        [showToast],
    );

    const verifyCoinSellerRechargeReceipt = async (payload) => {
        if (!session?.canVerifyCoinSellerRechargeOrder) {
            showToast("无校验币商充值订单权限", "error");
            return null;
        }
        try {
            const verification = await verifyFinanceCoinSellerRechargeReceipt(cleanPayload(payload));
            showToast(verification?.verified ? "订单号校验通过" : verification?.failureReason || "订单号校验未通过", verification?.verified ? "success" : "error");
            return verification;
        } catch (err) {
            showToast(err.message || "校验订单号失败", "error");
            return null;
        }
    };

    const verifyCoinSellerRechargeOrder = async (order) => {
        if (!order?.id || !session?.canVerifyCoinSellerRechargeOrder) {
            return;
        }
        setCoinSellerRechargeOrderActionLoading(`verify:${order.id}`);
        try {
            await verifyFinanceCoinSellerRechargeOrder(order.id);
            showToast("订单校验已完成", "success");
            await loadCoinSellerRechargeOrders();
        } catch (err) {
            showToast(err.message || "校验币商充值订单失败", "error");
        } finally {
            setCoinSellerRechargeOrderActionLoading("");
        }
    };

    const grantCoinSellerRechargeOrder = async (order) => {
        if (!order?.id || !session?.canGrantCoinSellerRechargeOrder) {
            return;
        }
        setCoinSellerRechargeOrderActionLoading(`grant:${order.id}`);
        try {
            await grantFinanceCoinSellerRechargeOrder(order.id);
            showToast("币商充值已发放", "success");
            await loadCoinSellerRechargeOrders();
        } catch (err) {
            showToast(err.message || "发放币商充值失败", "error");
        } finally {
            setCoinSellerRechargeOrderActionLoading("");
        }
    };

    const updateRechargeDetailFilters = (nextFilters) => {
        setRechargeDetailFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 }));
    };

    const updateWithdrawalFilters = (nextFilters) => {
        setWithdrawalFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 }));
    };

    const updateOperationsWithdrawalFilters = (nextFilters) => {
        setOperationsWithdrawalFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 }));
    };

    const changeActiveView = (nextView) => {
        // URL 只能进入当前会话有权访问的视图；无权深链按初始化回退处理，菜单点击则直接忽略越权目标。
        if (!canAccessFinanceView(session, nextView)) {
            return;
        }
        setActiveView(nextView);
        syncFinanceView(nextView);
    };

    const updateCoinSellerRechargeOrderFilters = (nextFilters) => {
        setCoinSellerRechargeOrderFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 }));
    };

    if (sessionState.loading) {
        return <StateScreen title="正在进入财务系统" />;
    }

    if (sessionState.error) {
        return (
            <StateScreen
                actionHref={buildLoginRedirectPath(window.location)}
                actionText="重新登录"
                title={sessionState.error}
            />
        );
    }

    if (!session?.canAccessFinance) {
        return <StateScreen title="无财务系统权限" />;
    }

    const presets = timePresets();
    const activePreset = presets.find(
        (preset) =>
            String(preset.range.startMs || "") === String(rechargeDetailFilters.timeRange.startMs || "") &&
            String(preset.range.endMs || "") === String(rechargeDetailFilters.timeRange.endMs || ""),
    );
    const isFinanceView = RECHARGE_VIEWS.has(activeView);
    const financeToolbar =
        isFinanceView && session.canViewAppRechargeDetails ? (
            <>
                <div className="finance-seg" role="group" aria-label="时间范围">
                    {presets.map((preset) => (
                        <button
                            className={activePreset?.key === preset.key ? "is-on" : ""}
                            key={preset.key}
                            type="button"
                            onClick={() => updateRechargeDetailFilters({ timeRange: preset.range })}
                        >
                            {preset.label}
                        </button>
                    ))}
                </div>
                <TimeRangeFilter
                    label="自定义 · 中国时区"
                    value={rechargeDetailFilters.timeRange}
                    onChange={(timeRange) => updateRechargeDetailFilters({ timeRange })}
                />
                <div className="finance-seg" role="group" aria-label="计价单位">
                    {[
                        ["usd", "USD"],
                        ["coin", "金币"],
                    ].map(([value, label]) => (
                        <button
                            className={rechargeDetailFilters.currency === value ? "is-on" : ""}
                            key={value}
                            type="button"
                            onClick={() =>
                                updateRechargeDetailFilters({ currency: value, page: rechargeDetailFilters.page })
                            }
                        >
                            {label}
                        </button>
                    ))}
                </div>
            </>
        ) : null;

    const appBar =
        isFinanceView && session.canViewAppRechargeDetails ? (
            <div className="finance-appbar">
                <button
                    className={["finance-appchip", rechargeDetailFilters.appCode === "" ? "is-on" : ""]
                        .filter(Boolean)
                        .join(" ")}
                    type="button"
                    onClick={() => updateRechargeDetailFilters({ appCode: "", regionId: "" })}
                >
                    全部 App
                </button>
                {rechargeApps.map((app) => (
                    <button
                        className={["finance-appchip", rechargeDetailFilters.appCode === app.appCode ? "is-on" : ""]
                            .filter(Boolean)
                            .join(" ")}
                        key={app.appCode}
                        type="button"
                        onClick={() => updateRechargeDetailFilters({ appCode: app.appCode, regionId: "" })}
                    >
                        <AppIdentity app={app} size="small" />
                    </button>
                ))}
            </div>
        ) : null;

    return (
        <FinanceShell
            activeView={activeView}
            appBar={appBar}
            canViewCoinSellerRechargeOrders={session.canViewCoinSellerRechargeOrders}
            canViewFinanceWithdrawals={session.canViewWithdrawalApplications}
            canViewOperationsWithdrawals={session.canViewOperationsWithdrawalApplications}
            canViewRechargeDetails={session.canViewAppRechargeDetails}
            loading={
                optionsState.loading ||
                rechargeDetailsState.loading ||
                withdrawalsState.loading ||
                operationsWithdrawalsState.loading ||
                coinSellerRechargeOrdersState.loading
            }
            session={session}
            toolbar={financeToolbar}
            onViewChange={changeActiveView}
        >
            {activeView === "overview" && session.canViewAppRechargeDetails ? (
                <FinanceOverview
                    apps={rechargeApps}
                    currency={rechargeDetailFilters.currency}
                    filters={rechargeDetailFilters}
                    loading={rechargeSummaryState.loading || rechargeOverviewState.loading}
                    overview={rechargeOverviewState.data}
                    perAppSummaries={rechargeSummaryState.perApp}
                    prevSummary={rechargeSummaryState.prev}
                    regions={rechargeOverviewState.data.regions}
                    summary={rechargeSummaryState.merged}
                    onFiltersChange={updateRechargeDetailFilters}
                    onGoUnsynced={() => {
                        updateRechargeDetailFilters({ paidState: "unsynced", rechargeType: "" });
                        changeActiveView("rechargeDetails");
                    }}
                    onOpenApp={(appCode) => {
                        updateRechargeDetailFilters({ appCode, regionId: "" });
                        changeActiveView("rechargeDetails");
                    }}
                    onOpenWithdrawals={() => changeActiveView("withdrawalFinance")}
                />
            ) : activeView === "coinSellerRechargeOrders" && session.canViewCoinSellerRechargeOrders ? (
                <FinanceCoinSellerRechargeOrderList
                    actionLoading={coinSellerRechargeOrderActionLoading}
                    apps={rechargeApps}
                    canCreate={session.canCreateCoinSellerRechargeOrder}
                    canConfigureExchangeRate={session.canConfigureCoinSellerRechargeExchangeRate}
                    canGrant={session.canGrantCoinSellerRechargeOrder}
                    canUpdateUSDTAddress={session.canUpdateUSDTAddress}
                    canVerify={session.canVerifyCoinSellerRechargeOrder}
                    error={coinSellerRechargeOrdersState.error}
                    filters={coinSellerRechargeOrderFilters}
                    loading={coinSellerRechargeOrdersState.loading}
                    orders={coinSellerRechargeOrdersState.data}
                    onCreate={submitCoinSellerRechargeOrder}
                    onLoadExchangeRate={loadCoinSellerRechargeExchangeRate}
                    onLoadUSDTAddresses={loadUSDTAddresses}
                    onFiltersChange={updateCoinSellerRechargeOrderFilters}
                    onGrant={grantCoinSellerRechargeOrder}
                    onReload={loadCoinSellerRechargeOrders}
                    onQuote={quoteCoinSellerRechargeOrder}
                    onSaveExchangeRate={saveCoinSellerRechargeExchangeRate}
                    onSaveUSDTAddress={saveUSDTAddress}
                    onVerify={verifyCoinSellerRechargeOrder}
                    onVerifyReceipt={verifyCoinSellerRechargeReceipt}
                />
            ) : activeView === "recon" && session.canViewAppRechargeDetails ? (
                <FinanceReconciliation
                    loading={reconState.loading}
                    month={reconMonthMeta(reconMonthOffset)}
                    overview={reconState.overview}
                    summary={reconState.summary}
                    onMonthChange={(delta) => setReconMonthOffset((current) => Math.min(0, current + delta))}
                />
            ) : activeView === "rechargeDetails" && session.canViewAppRechargeDetails ? (
                <FinanceRechargeDetailList
                    apps={rechargeApps}
                    bills={rechargeDetailsState.data}
                    currency={rechargeDetailFilters.currency}
                    error={rechargeDetailsState.error}
                    exporting={rechargeExporting}
                    filters={rechargeDetailFilters}
                    googlePaidRefreshing={googlePaidRefreshing}
                    loading={rechargeDetailsState.loading}
                    regions={rechargeRegionsState.byApp[rechargeAppCode] || []}
                    regionsByApp={rechargeRegionsState.byApp}
                    summary={rechargeSummaryState.merged}
                    onExport={exportRechargeBills}
                    onFiltersChange={updateRechargeDetailFilters}
                    onRefreshGooglePaid={refreshGooglePaid}
                    onReload={() => {
                        loadRechargeDetails();
                        loadRechargeSummary();
                        loadRechargeOverview();
                    }}
                />
            ) : activeView === "withdrawalOperations" && session.canViewOperationsWithdrawalApplications ? (
                <FinanceWithdrawalApplicationList
                    applications={operationsWithdrawalsState.data}
                    canAudit={session.canAuditOperationsWithdrawalApplications}
                    error={operationsWithdrawalsState.error || withdrawalOptionsState.error}
                    filters={operationsWithdrawalFilters}
                    loading={operationsWithdrawalsState.loading}
                    options={withdrawalOptionsState.data}
                    reviewStage="operations"
                    onAudit={setWithdrawalAuditTarget}
                    onFiltersChange={updateOperationsWithdrawalFilters}
                    onReload={loadOperationsWithdrawals}
                />
            ) : activeView === "withdrawalFinance" && session.canViewWithdrawalApplications ? (
                <FinanceWithdrawalApplicationList
                    applications={withdrawalsState.data}
                    canAudit={session.canAuditWithdrawalApplications}
                    error={withdrawalsState.error || withdrawalOptionsState.error}
                    filters={withdrawalFilters}
                    loading={withdrawalsState.loading}
                    options={withdrawalOptionsState.data}
                    reviewStage="finance"
                    onAudit={setWithdrawalAuditTarget}
                    onFiltersChange={updateWithdrawalFilters}
                    onReload={loadWithdrawals}
                />
            ) : null}
            <FinanceAuditDialog
                application={withdrawalAuditTarget}
                apps={withdrawalOptionsState.data.apps}
                loading={withdrawalAuditLoading}
                open={Boolean(withdrawalAuditTarget)}
                requireApprovalImage={withdrawalAuditTarget?.reviewStage !== "operations"}
                requireRejectRemark
                reviewLabel={withdrawalAuditTarget?.reviewStage === "operations" ? "运营" : "财务"}
                onClose={() => setWithdrawalAuditTarget(null)}
                onSubmit={submitWithdrawalAudit}
            />
        </FinanceShell>
    );
}

function StateScreen({ actionHref = "", actionText = "", title }) {
    return (
        <main className="finance-state-screen">
            <section className="finance-state-panel">
                <h1>{title}</h1>
                {actionHref ? <a href={actionHref}>{actionText}</a> : null}
            </section>
        </main>
    );
}

function cleanPayload(payload) {
    return Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== ""));
}
