import AppsOutlined from "@mui/icons-material/AppsOutlined";
import CardGiftcardOutlined from "@mui/icons-material/CardGiftcardOutlined";
import CasinoOutlined from "@mui/icons-material/CasinoOutlined";
import InsightsOutlined from "@mui/icons-material/InsightsOutlined";
import RefreshOutlined from "@mui/icons-material/RefreshOutlined";
import { useCallback, useEffect, useMemo, useState } from "react";
import { PERMISSIONS } from "@/app/permissions";
import { Button } from "@/shared/ui/Button.jsx";
import { DataState } from "@/shared/ui/DataState.jsx";
import { PageHead } from "@/shared/ui/PageHead.jsx";
import { useToast } from "@/shared/ui/ToastProvider.jsx";
import { adjustLuckyGiftPool, DEFAULT_POOL_ID, fetchOpsDashboard, saveLuckyGiftConfig } from "./api.js";
import { AppsView } from "./components/AppsView.jsx";
import { ConfigsView } from "./components/ConfigsView.jsx";
import { DrawsView } from "./components/DrawsView.jsx";
import { LuckyGiftConfigDialog } from "./components/LuckyGiftConfigDialog.jsx";
import { LuckyGiftPoolAdjustmentDialog } from "./components/LuckyGiftPoolAdjustmentDialog.jsx";
import { OpsShell } from "./components/OpsShell.jsx";
import { OverviewView } from "./components/OverviewView.jsx";
import { formatNumber } from "./format.js";

const views = [
    { icon: InsightsOutlined, key: "overview", label: "数据总览" },
    { icon: CardGiftcardOutlined, key: "configs", label: "幸运礼物配置" },
    { icon: CasinoOutlined, key: "draws", label: "抽奖记录" },
    { icon: AppsOutlined, key: "apps", label: "应用列表" },
];
const viewKeys = new Set(views.map((view) => view.key));

const initialDashboard = {
    apps: [],
    appSummaries: [],
    luckyGifts: [],
    poolBalances: [],
    summary: {},
};

export function initialOpsCenterView(search = globalThis.location?.search || "") {
    const requested = new URLSearchParams(search).get("view") || "";
    return viewKeys.has(requested) ? requested : "overview";
}

const denyPermission = () => false;

export function OpsCenterApp({ can = denyPermission }) {
    // 主后台旧入口通过 ?view=configs 进入独立 HTML；只接受白名单视图，异常参数仍回到总览。
    const [activeView, setActiveView] = useState(() => initialOpsCenterView());
    const [state, setState] = useState({ data: initialDashboard, error: "", loaded: false, loading: true });
    const [dialog, setDialog] = useState(null);
    const [poolDialog, setPoolDialog] = useState(null);
    const [adjustingPool, setAdjustingPool] = useState(false);
    const [savingConfig, setSavingConfig] = useState(false);
    const { showToast } = useToast();
    const canCreditPool = can(PERMISSIONS.luckyGiftPoolCredit);
    const canDebitPool = can(PERMISSIONS.luckyGiftPoolDebit);
    const canUpdateConfig = can(PERMISSIONS.luckyGiftUpdate);

    const loadDashboard = useCallback(async () => {
        setState((current) => ({ ...current, error: "", loading: true }));
        try {
            const data = await fetchOpsDashboard();
            setState({ data, error: "", loaded: true, loading: false });
        } catch (error) {
            // 接口灰度期间保留已加载的数据和页面结构，单次失败只提示、不整页白屏。
            setState((current) => ({
                data: current.data || initialDashboard,
                error: error.message || "运营接口暂不可用",
                loaded: current.loaded,
                loading: false,
            }));
        }
    }, []);

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

    const { data } = state;

    const appOptions = useMemo(
        () =>
            data.apps.map((item) => [
                item.app_code ?? item.appCode,
                `${item.app_name ?? item.appName ?? ""} (${item.app_code ?? item.appCode})`,
            ]),
        [data.apps],
    );

    // 抽奖记录页的奖池下拉：配置和真实水位都可能先于对方存在（先发布未入账 / 老池子停用后仍有流水），取并集。
    const poolIDsByApp = useMemo(() => {
        const byApp = new Map();
        const add = (appCode, poolID) => {
            if (!appCode || !poolID) {
                return;
            }
            if (!byApp.has(appCode)) {
                byApp.set(appCode, new Set());
            }
            byApp.get(appCode).add(poolID);
        };
        data.luckyGifts.forEach((config) => add(config.app_code, config.pool_id));
        data.poolBalances.forEach((pool) => add(pool.app_code ?? pool.appCode, pool.pool_id ?? pool.poolId));
        return new Map(Array.from(byApp.entries(), ([appCode, poolIDs]) => [appCode, Array.from(poolIDs).sort()]));
    }, [data.luckyGifts, data.poolBalances]);

    const openCreateConfig = useCallback(() => {
        const firstAppCode = data.apps
            .map((item) =>
                String(item.app_code ?? item.appCode ?? "")
                    .trim()
                    .toLowerCase(),
            )
            .find(Boolean);
        if (!firstAppCode) {
            showToast("暂无可添加配置的应用", "warning");
            return;
        }
        // 金额门槛和六维上限按 App 金币口径配置，绝不能复制第一条已发布规则给另一个 App。
        // 仅传规则身份，让 configToForm 生成 disabled dynamic_v3 安全草稿；金额字段保持 0，运营补齐后才能启用。
        setDialog({
            config: {
                app_code: firstAppCode,
                enabled: false,
                is_default: true,
                pool_id: DEFAULT_POOL_ID,
                rule_version: 0,
                strategy_version: "dynamic_v3",
            },
            mode: "create",
        });
    }, [data.apps, showToast]);

    const openEditConfig = useCallback((config) => {
        setDialog({ config: structuredClone(config), mode: "edit" });
    }, []);

    const openPoolAdjustment = useCallback((pool, direction) => {
        if (!pool) {
            return;
        }
        // 幂等键绑定一次弹窗会话，而不是一次 fetch；网络超时或 409 后弹窗保留，后续重试不会重复入账。
        setPoolDialog({
            adjustmentId: createPoolAdjustmentID(),
            direction,
            pool: structuredClone(pool),
        });
    }, []);

    const handleSaveConfig = useCallback(
        async (config) => {
            setSavingConfig(true);
            try {
                await saveLuckyGiftConfig(config);
                showToast(`已发布 ${config.app_code} / ${config.pool_id} 的新规则版本`, "success");
                setDialog(null);
                await loadDashboard();
            } catch (error) {
                // 保存失败保持弹窗打开，运营修完参数可直接重试，不丢已填内容。
                showToast(error.message || "保存幸运礼物配置失败", "error");
            } finally {
                setSavingConfig(false);
            }
        },
        [loadDashboard, showToast],
    );

    const handleAdjustPool = useCallback(
        async (request) => {
            setAdjustingPool(true);
            try {
                const result = await adjustLuckyGiftPool(request);
                const adjustment = result.adjustment || {};
                const action = request.direction === "out" ? "扣减" : "添加";
                showToast(
                    `已${action}水位 ${request.appCode} / ${request.poolId} / ${request.strategyVersion}：${formatNumber(
                        adjustment.balance_before,
                    )} → ${formatNumber(adjustment.balance_after)}`,
                    "success",
                );
                setPoolDialog(null);
                await loadDashboard();
            } catch (error) {
                // owner 可能已经成功但响应丢失；保留表单与 adjustment_id，用户重试时由 owner 返回首次结果而不重复调整。
                showToast(error.message || "调整奖池水位失败", "error");
            } finally {
                setAdjustingPool(false);
            }
        },
        [loadDashboard, showToast],
    );

    const defaultAppCode = data.apps[0]?.app_code ?? data.apps[0]?.appCode ?? "";

    return (
        <OpsShell activeView={activeView} onViewChange={setActiveView} views={views}>
            <div className="ops-page">
                <PageHead meta="跨应用幸运礼物配置与抽奖数据面板" title="运营配置中心">
                    <span className={state.loading ? "ops-status is-loading" : "ops-status"}>
                        {state.loading ? "同步中" : "已就绪"}
                    </span>
                    <Button
                        disabled={state.loading}
                        startIcon={<RefreshOutlined fontSize="small" />}
                        onClick={loadDashboard}
                    >
                        刷新
                    </Button>
                </PageHead>
                {/* 首屏失败走 DataState 的错误重试页；已有数据时刷新失败只弹提示条，保留旧数据可用。 */}
                {state.error && state.loaded ? (
                    <div className="ops-alert" role="alert">
                        {state.error}
                    </div>
                ) : null}
                <DataState
                    error={state.loaded ? "" : state.error}
                    loading={state.loading && !state.loaded}
                    onRetry={loadDashboard}
                >
                    {activeView === "overview" ? (
                        <OverviewView
                            canCredit={canCreditPool}
                            canDebit={canDebitPool}
                            data={data}
                            disabled={adjustingPool || state.loading}
                            onCredit={(pool) => openPoolAdjustment(pool, "in")}
                            onDebit={(pool) => openPoolAdjustment(pool, "out")}
                        />
                    ) : null}
                    {activeView === "configs" ? (
                        <ConfigsView
                            apps={data.apps}
                            canCredit={canCreditPool}
                            canDebit={canDebitPool}
                            canUpdate={canUpdateConfig}
                            disabled={adjustingPool || state.loading}
                            luckyGifts={data.luckyGifts}
                            poolBalances={data.poolBalances}
                            saving={savingConfig}
                            onCreate={openCreateConfig}
                            onCredit={(pool) => openPoolAdjustment(pool, "in")}
                            onDebit={(pool) => openPoolAdjustment(pool, "out")}
                            onEdit={openEditConfig}
                        />
                    ) : null}
                    {activeView === "draws" ? (
                        <DrawsView apps={data.apps} defaultAppCode={defaultAppCode} poolIDsByApp={poolIDsByApp} />
                    ) : null}
                    {activeView === "apps" ? <AppsView apps={data.apps} /> : null}
                </DataState>
                {dialog ? (
                    <LuckyGiftConfigDialog
                        appOptions={appOptions}
                        config={dialog.config}
                        mode={dialog.mode}
                        saving={savingConfig}
                        onClose={() => setDialog(null)}
                        onSubmit={handleSaveConfig}
                    />
                ) : null}
                {poolDialog ? (
                    <LuckyGiftPoolAdjustmentDialog
                        adjustmentId={poolDialog.adjustmentId}
                        direction={poolDialog.direction}
                        pool={poolDialog.pool}
                        submitting={adjustingPool}
                        onClose={() => setPoolDialog(null)}
                        onSubmit={handleAdjustPool}
                    />
                ) : null}
            </div>
        </OpsShell>
    );
}

let fallbackAdjustmentSequence = 0;

function createPoolAdjustmentID() {
    if (typeof globalThis.crypto?.randomUUID === "function") {
        return globalThis.crypto.randomUUID();
    }
    fallbackAdjustmentSequence += 1;
    return `pool-adjust-${Date.now().toString(36)}-${fallbackAdjustmentSequence.toString(36)}`;
}
