DailyAssets Plus

记录每日总资产增长,包含详细统计功能:当前资产、日环比、瞬时时薪、近7天均增速、近7天胜率、近30天日均、最佳/最差日、上一次翻倍等,支持数据导入导出(完全兼容Everyday Profit Pro格式)Record daily total asset growth, including detailed statistics: current assets, daily change, hourly rate, 7-day average growth rate, 7-day win rate, 30-day daily average, best/worst day, last doubling time, etc. Support data import/export (fully compatible with Everyday Profit Pro format).

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         DailyAssets Plus
// @namespace    http://tampermonkey.net/
// @version      1.0.8
// @description  记录每日总资产增长,包含详细统计功能:当前资产、日环比、瞬时时薪、近7天均增速、近7天胜率、近30天日均、最佳/最差日、上一次翻倍等,支持数据导入导出(完全兼容Everyday Profit Pro格式)Record daily total asset growth, including detailed statistics: current assets, daily change, hourly rate, 7-day average growth rate, 7-day win rate, 30-day daily average, best/worst day, last doubling time, etc. Support data import/export (fully compatible with Everyday Profit Pro format).
// @author       VictoryWinWinWin, PaperCat, Vicky718, SuXingX, ColaCola
// @match        https://www.milkywayidle.com/*
// @match        https://www.milkywayidlecn.com/*
// @match        https://test.milkywayidle.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    /* =========================
       语言配置
    ========================= */
    const LANGUAGES = {
        zh: {
            assetsGrowth: '💰总资产增长',
            historyIcon: '显示详细资产历史图表',
            metricsIconShow: '显示统计指标',
            metricsIconHide: '隐藏统计指标',
            settingsIcon: '图表设置',
            avg7Days: '近7天日均',
            lastRecord: '最近记录',
            chartTitle: '资产历史曲线',
            timeRange: '时间范围:',
            settingsTitle: '资产图表设置',
            viewMode: '视图模式',
            summaryView: '摘要视图',
            detailedView: '详细视图',
            summaryOptions: '摘要视图显示选项',
            showCurrent: '显示流动资产',
            showNonCurrent: '显示非流动资产',
            detailedOptions: '详细视图显示选项',
            showEquipped: '显示装备价值',
            showInventory: '显示库存价值',
            showMarket: '显示订单价值',
            showHouse: '显示房子价值',
            showAbility: '显示技能价值',
            generalOptions: '通用显示选项',
            showTotal: '显示总资产',
            unitSettings: '资产显示单位',
            unitMode: '单位模式:',
            unitAuto: '自动',
            unitK: '千(K)',
            unitM: '百万(M)',
            unitB: '十亿(B)',
            timeRangeSettings: '时间范围设置',
            showTimeRange: '显示时间范围选择器',
            availableTimeRanges: '可用时间范围:',
            currentButtons: '当前可用的时间范围按钮:',
            importExport: '📦 数据导入导出',
            exportAll: '导出全部(含设置)',
            exportData: '仅导出数据',
            importData: '导入数据',
            preview: '导入预览',
            version: '版本',
            exportTime: '导出时间',
            roleCount: '角色数量',
            totalRecords: '总记录数',
            breakdownRecords: '分项资产记录',
            tagRecords: '标签记录',
            containsSettings: '包含设置',
            yes: '是',
            no: '否',
            roleStats: '角色数据统计:',
            records: '条记录',
            confirmImport: '确认导入',
            cancel: '取消',
            exportSuccess: '数据已导出到',
            exportFailed: '导出失败',
            importFailed: '导入失败',
            noImportData: '没有可导入的数据',
            invalidFormat: '无效的数据格式',
            unrecognizedVersion: '无法识别的备份文件版本',
            missingAssetData: '备份文件中缺少资产数据',
            previewFailed: '预览失败',
            currentAssets: '当前资产',
            recentUpdate: '最近更新',
            dailyChange: '日环比',
            hourlyRate: '瞬时时薪',
            hourlyRateDesc: '按当日平均',
            avg7Growth: '近7天均增速',
            avg7Desc: '日均增率 / 日均净变动',
            winRate7: '近7天胜率',
            avg30Daily: '近30天日均',
            bestWorstDay: '最佳/最差日',
            lastDouble: '上一次翻倍',
            noRecord: '尚无记录',
            doubling: '翻倍',
            target: '目标',
            days: '天',
            profitLossFlat: '盈/亏/平',
            equipmentValue: '装备价值',
            inventoryValue: '库存价值',
            marketValue: '订单价值',
            houseValue: '房子价值',
            abilityValue: '技能价值',
            currentAssetsValue: '流动资产',
            nonCurrentAssetsValue: '非流动资产',
            totalAssets: '总资产',
        },
        en: {
            assetsGrowth: '💰Total Asset Growth',
            historyIcon: 'Show asset history chart',
            metricsIconShow: 'Show statistics',
            metricsIconHide: 'Hide statistics',
            settingsIcon: 'Chart settings',
            avg7Days: '7-day avg',
            lastRecord: 'Last record',
            chartTitle: 'Asset History Chart',
            timeRange: 'Time range:',
            settingsTitle: 'Asset Chart Settings',
            viewMode: 'View Mode',
            summaryView: 'Summary View',
            detailedView: 'Detailed View',
            summaryOptions: 'Summary View Options',
            showCurrent: 'Show Current Assets',
            showNonCurrent: 'Show Non-Current Assets',
            detailedOptions: 'Detailed View Options',
            showEquipped: 'Show Equipment Value',
            showInventory: 'Show Inventory Value',
            showMarket: 'Show Market Value',
            showHouse: 'Show House Value',
            showAbility: 'Show Ability Value',
            generalOptions: 'General Options',
            showTotal: 'Show Total Assets',
            unitSettings: 'Asset Display Unit',
            unitMode: 'Unit mode:',
            unitAuto: 'Auto',
            unitK: 'K',
            unitM: 'M',
            unitB: 'B',
            timeRangeSettings: 'Time Range Settings',
            showTimeRange: 'Show time range selector',
            availableTimeRanges: 'Available time ranges:',
            currentButtons: 'Current time range buttons:',
            importExport: '📦 Data Import/Export',
            exportAll: 'Export All (with settings)',
            exportData: 'Export Data Only',
            importData: 'Import Data',
            preview: 'Import Preview',
            version: 'Version',
            exportTime: 'Export time',
            roleCount: 'Characters',
            totalRecords: 'Total records',
            breakdownRecords: 'Breakdown records',
            tagRecords: 'Tag records',
            containsSettings: 'Contains settings',
            yes: 'Yes',
            no: 'No',
            roleStats: 'Character statistics:',
            records: 'records',
            confirmImport: 'Confirm Import',
            cancel: 'Cancel',
            exportSuccess: 'Data exported to',
            exportFailed: 'Export failed',
            importFailed: 'Import failed',
            noImportData: 'No data to import',
            invalidFormat: 'Invalid data format',
            unrecognizedVersion: 'Unrecognized backup version',
            missingAssetData: 'Missing asset data in backup',
            previewFailed: 'Preview failed',
            currentAssets: 'Current Assets',
            recentUpdate: 'Last update',
            dailyChange: 'Daily Change',
            hourlyRate: 'Hourly Rate',
            hourlyRateDesc: 'Based on daily average',
            avg7Growth: '7-day Avg Growth',
            avg7Desc: 'Avg rate / Avg change',
            winRate7: '7-day Win Rate',
            avg30Daily: '30-day Daily Avg',
            bestWorstDay: 'Best/Worst Day',
            lastDouble: 'Last Double',
            noRecord: 'No record',
            doubling: 'Double in',
            target: 'Target',
            days: 'd',
            profitLossFlat: 'Win/Loss/Flat',
            equipmentValue: 'Equipment Value',
            inventoryValue: 'Inventory Value',
            marketValue: 'Market Value',
            houseValue: 'House Value',
            abilityValue: 'Ability Value',
            currentAssetsValue: 'Current Assets',
            nonCurrentAssetsValue: 'Non-Current Assets',
            totalAssets: 'Total Assets',
        }
    };

    function getCurrentLanguage() {
        const lang = GM_getValue('dailyAssetsLanguage', 'zh');
        return lang === 'en' ? LANGUAGES.en : LANGUAGES.zh;
    }

    function toggleLanguage() {
        const currentLang = GM_getValue('dailyAssetsLanguage', 'zh');
        const newLang = currentLang === 'zh' ? 'en' : 'zh';
        GM_setValue('dailyAssetsLanguage', newLang);
        setTimeout(() => location.reload(), 100);
    }

    function t(key) {
        const lang = getCurrentLanguage();
        return lang[key] || key;
    }

    /* =========================
       常量与存储键定义
    ========================= */
    const STORAGE_KEYS = {
        assetData: 'kbd_asset_data_v2',
        metricsPrefs: 'kbd_metrics_prefs',
        metricsPanel: 'kbd_metrics_panel',
        lastUpdate: 'kbd_last_update_at',
    };

    // Everyday Profit Pro 的存储键
    const EP_STORAGE_KEYS = {
        totalData: 'kbd_calc_data',
        tags: 'kbd_calc_tags',
        tagPrefs: 'kbd_calc_tag_prefs',
        tagPanel: 'kbd_calc_tag_panel',
        dataPanel: 'kbd_calc_data_panel',
        lastUpdate: 'kbd_calc_last_update_at',
        breakdownData: 'kbd_calc_breakdown_data',
    };

    /* =========================
       工具函数
    ========================= */
    const safeJsonParse = (raw, fallback) => {
        try { return raw ? JSON.parse(raw) : fallback; } catch { return fallback; }
    };

    const readPrefs = (key) => safeJsonParse(localStorage.getItem(key), {});
    const writePrefs = (key, prefs) => localStorage.setItem(key, JSON.stringify(prefs));

    const getRoleBoolPref = (key, roleId, defaultValue) => {
        const prefs = readPrefs(key);
        if (roleId && Object.prototype.hasOwnProperty.call(prefs, roleId)) return !!prefs[roleId];
        return !!defaultValue;
    };

    const setRoleBoolPref = (key, roleId, value) => {
        if (!roleId) return;
        const prefs = readPrefs(key);
        prefs[roleId] = !!value;
        writePrefs(key, prefs);
    };

    const readRoleLastUpdateMap = () => safeJsonParse(localStorage.getItem(STORAGE_KEYS.lastUpdate), {});
    const writeRoleLastUpdateMap = (map) => localStorage.setItem(STORAGE_KEYS.lastUpdate, JSON.stringify(map || {}));
    const setRoleLastUpdate = (roleId, iso = new Date().toISOString()) => {
        if (!roleId) return;
        const map = readRoleLastUpdateMap();
        map[roleId] = iso;
        writeRoleLastUpdateMap(map);
    };
    const getRoleLastUpdate = (roleId) => {
        if (!roleId) return null;
        const map = readRoleLastUpdateMap();
        return map && map[roleId] ? map[roleId] : null;
    };

    const formatIsoToLocalDateTime = (iso) => {
        if (!iso) return '';
        const d = new Date(iso);
        if (!Number.isFinite(d.getTime())) return String(iso);
        const pad = (n) => String(n).padStart(2, '0');
        return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
    };

    function parseFormattedNumber(str) {
        if (!str) return 0;
        const match = String(str).match(/(-?[\d.,]+)\s*([kKmMbBtT]?)/);
        if (!match) return 0;

        let [, numericPart, unit = ''] = match;
        numericPart = numericPart.replace(/\s+/g, '');
        if (!numericPart) return 0;

        const commaCount = (numericPart.match(/,/g) || []).length;
        const dotCount = (numericPart.match(/\./g) || []).length;

        if (commaCount && dotCount) {
            if (numericPart.lastIndexOf('.') > numericPart.lastIndexOf(',')) {
                numericPart = numericPart.replace(/,/g, '');
            } else {
                numericPart = numericPart.replace(/\./g, '');
                numericPart = numericPart.replace(/,/g, '.');
            }
        } else if (commaCount) {
            if (commaCount === 1 && numericPart.split(',')[1]?.length <= 2) numericPart = numericPart.replace(',', '.');
            else numericPart = numericPart.replace(/,/g, '');
        } else if (dotCount > 1) {
            const parts = numericPart.split('.');
            const decimal = parts.pop();
            numericPart = parts.join('') + (decimal ? `.${decimal}` : '');
        }

        const num = parseFloat(numericPart);
        if (isNaN(num)) return 0;

        const multiplierMap = { k: 1e3, m: 1e6, b: 1e9, t: 1e12 };
        const multiplier = multiplierMap[(unit || '').toLowerCase()] || 1;
        return num * multiplier;
    }

    function formatLargeNumber(num, unitMode = 'auto') {
        const n = Number(num) || 0;
        const abs = Math.abs(n);

        switch(unitMode) {
            case 'k':
                return (n / 1e3).toFixed(2) + 'K';
            case 'm':
                return (n / 1e6).toFixed(2) + 'M';
            case 'b':
                return (n / 1e9).toFixed(2) + 'B';
            case 'auto':
            default:
                if (abs >= 1e12) return (n / 1e12).toFixed(2) + 'T';
                if (abs >= 1e9) return (n / 1e9).toFixed(2) + 'B';
                if (abs >= 1e6) return (n / 1e6).toFixed(2) + 'M';
                if (abs >= 1e3) return (n / 1e3).toFixed(2) + 'K';
                return n.toFixed(2);
        }
    }

    const formatSignedLargeNumber = (num, unitMode = 'auto') => {
        const n = Number(num) || 0;
        return n > 0 ? `+${formatLargeNumber(n, unitMode)}` : formatLargeNumber(n, unitMode);
    };

    function normalizeColor(color) {
        if (!color) return '#888888';
        if (color.length === 4 && color[0] === '#') {
            return '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
        }
        return color;
    }

    /* =========================
       获取MWITools显示的数据
    ========================= */
    function getMWIToolsValues() {
        return new Promise((resolve) => {
            const checkElement = () => {
                const netWorthDetails = document.getElementById('netWorthDetails');
                if (netWorthDetails && netWorthDetails.children.length > 0) {
                    const values = {
                        equippedNetworth: 0,
                        inventoryNetworth: 0,
                        marketListingsNetworth: 0,
                        totalHouseScore: 0,
                        abilityScore: 0
                    };

                    try {
                        if (window.MWITools && window.MWITools.character) {
                            const char = window.MWITools.character;
                            values.equippedNetworth = char.equippedNetworth || 0;
                            values.inventoryNetworth = char.inventoryNetworth || 0;
                            values.marketListingsNetworth = char.marketListingsNetworth || 0;
                            values.totalHouseScore = char.totalHouseScore || 0;
                            values.abilityScore = char.abilityScore || 0;

                            console.log('[DailyAssets] 从 MWITools 读取:', values);
                            resolve(values);
                            return;
                        }

                        const divs = netWorthDetails.querySelectorAll('div');
                        let foundCount = 0;

                        divs.forEach(div => {
                            const text = div.textContent;

                            if (values.equippedNetworth === 0 &&
                                (text.includes('装备价值') || text.includes('Equipment value') || text.includes('Equipped value'))) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) {
                                    values.equippedNetworth = parseFormattedNumber(match[1]);
                                    foundCount++;
                                }
                            }
                            else if (values.inventoryNetworth === 0 &&
                                    (text.includes('库存价值') || text.includes('Inventory value'))) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) {
                                    values.inventoryNetworth = parseFormattedNumber(match[1]);
                                    foundCount++;
                                }
                            }
                            else if (values.marketListingsNetworth === 0 &&
                                    (text.includes('订单价值') || text.includes('Market listing value') || text.includes('Order value'))) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) {
                                    values.marketListingsNetworth = parseFormattedNumber(match[1]);
                                    foundCount++;
                                }
                            }
                            else if (values.totalHouseScore === 0 &&
                                    (text.includes('房子价值') || text.includes('Houses value') || text.includes('House value'))) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) {
                                    values.totalHouseScore = parseFormattedNumber(match[1]);
                                    foundCount++;
                                }
                            }
                            else if (values.abilityScore === 0 &&
                                    (text.includes('技能价值') || text.includes('Abilities value') || text.includes('Skill value'))) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) {
                                    values.abilityScore = parseFormattedNumber(match[1]);
                                    foundCount++;
                                }
                            }
                        });

                        if (foundCount >= 4) {
                            console.log('[DailyAssets] 从 DOM 读取成功:', values);
                            resolve(values);
                        } else {
                            console.log('[DailyAssets] 未找到足够字段,尝试智能解析');

                            const currentAssets = document.querySelector('#currentAssets')?.textContent || '';
                            const nonCurrentAssets = document.querySelector('#nonCurrentAssets')?.textContent || '';

                            const currentNumbers = currentAssets.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/g) || [];
                            const nonCurrentNumbers = nonCurrentAssets.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/g) || [];

                            if (currentNumbers.length >= 3) {
                                values.equippedNetworth = parseFormattedNumber(currentNumbers[0]);
                                values.inventoryNetworth = parseFormattedNumber(currentNumbers[1]);
                                values.marketListingsNetworth = parseFormattedNumber(currentNumbers[2]);
                            }

                            if (nonCurrentNumbers.length >= 2) {
                                values.totalHouseScore = parseFormattedNumber(nonCurrentNumbers[0]);
                                values.abilityScore = parseFormattedNumber(nonCurrentNumbers[1]);
                            }

                            console.log('[DailyAssets] 智能解析结果:', values);
                            resolve(values);
                        }

                    } catch (error) {
                        console.error('[DailyAssets] 读取错误:', error);
                        resolve(values);
                    }
                } else {
                    setTimeout(checkElement, 1000);
                }
            };

            checkElement();
        });
    }

    /* =========================
       数据存储类
    ========================= */
    class AssetDataStore {
        constructor(storageKey = STORAGE_KEYS.assetData, maxDays = 180, currentRole = 'default') {
            this.storageKey = storageKey;
            this.maxDays = maxDays;
            this.currentRole = currentRole;
            this.data = this.loadFromStorage();
        }

        setRole(roleId) {
            this.currentRole = roleId;
        }

        getRoleData() {
            if (!this.data[this.currentRole]) {
                this.data[this.currentRole] = {};
            }
            return this.data[this.currentRole];
        }

        getTodayKey() {
            const now = new Date();
            const utcPlus8 = new Date(now.getTime() + 8 * 3600000);
            return utcPlus8.toISOString().split('T')[0];
        }

        getYesterdayKey() {
            const now = new Date();
            const yesterday = new Date(now.getTime() - 24 * 3600000);
            const utcPlus8 = new Date(yesterday.getTime() + 8 * 3600000);
            return utcPlus8.toISOString().split('T')[0];
        }

        loadFromStorage() {
            return safeJsonParse(localStorage.getItem(this.storageKey), {});
        }

        saveToStorage() {
            localStorage.setItem(this.storageKey, JSON.stringify(this.data));
        }

        setTodayDetailedValues(equipped, inventory, market, house, ability) {
            const roleData = this.getRoleData();
            const today = this.getTodayKey();

            const totalAssets = equipped + inventory + market + house + ability;
            const currentAssets = equipped + inventory + market;
            const nonCurrentAssets = house + ability;

            roleData[today] = {
                equippedNetworth: equipped,
                inventoryNetworth: inventory,
                marketListingsNetworth: market,
                totalHouseScore: house,
                abilityScore: ability,
                currentAssets: currentAssets,
                nonCurrentAssets: nonCurrentAssets,
                totalAssets: totalAssets,
                timestamp: Date.now()
            };

            this.saveToEPFormat(today, equipped, inventory, market, house, ability, totalAssets);

            this.cleanupOldData();
            this.saveToStorage();
        }

        saveToEPFormat(date, equip, inventory, orders, house, skill, total) {
            const role = this.currentRole;

            // 保存总资产到 kbd_calc_data
            const epTotalData = safeJsonParse(localStorage.getItem(EP_STORAGE_KEYS.totalData), {});
            if (!epTotalData[role]) epTotalData[role] = {};
            epTotalData[role][date] = total;
            localStorage.setItem(EP_STORAGE_KEYS.totalData, JSON.stringify(epTotalData));

            // 保存分项数据到 kbd_calc_breakdown_data
            const epBreakdownData = safeJsonParse(localStorage.getItem(EP_STORAGE_KEYS.breakdownData), {});
            if (!epBreakdownData[role]) epBreakdownData[role] = {};

            epBreakdownData[role][date] = {
                equip: equip,
                inventory: inventory,
                orders: orders,
                house: house,
                skill: skill
            };

            localStorage.setItem(EP_STORAGE_KEYS.breakdownData, JSON.stringify(epBreakdownData));

            console.log('[DailyAssets] 已保存到 EP 格式:', {
                role: role,
                date: date,
                total: total,
                breakdown: { equip, inventory, orders, house, skill }
            });
        }

        cleanupOldData() {
            const roleData = this.getRoleData();
            const keys = Object.keys(roleData).sort();
            const cutoff = Date.now() - (this.maxDays * 24 * 3600 * 1000);

            const newData = {};
            keys.forEach(key => {
                if (roleData[key].timestamp > cutoff) {
                    newData[key] = roleData[key];
                }
            });
            this.data[this.currentRole] = newData;
        }

        getTodayDeltas() {
            const roleData = this.getRoleData();
            const todayKey = this.getTodayKey();
            const yesterdayKey = this.getYesterdayKey();

            const todayData = roleData[todayKey] || {
                equippedNetworth: 0, inventoryNetworth: 0, marketListingsNetworth: 0,
                totalHouseScore: 0, abilityScore: 0,
                currentAssets: 0, nonCurrentAssets: 0, totalAssets: 0
            };

            const yesterdayData = roleData[yesterdayKey] || {
                equippedNetworth: 0, inventoryNetworth: 0, marketListingsNetworth: 0,
                totalHouseScore: 0, abilityScore: 0,
                currentAssets: 0, nonCurrentAssets: 0, totalAssets: 0
            };

            return {
                equippedDelta: todayData.equippedNetworth - yesterdayData.equippedNetworth,
                inventoryDelta: todayData.inventoryNetworth - yesterdayData.inventoryNetworth,
                marketDelta: todayData.marketListingsNetworth - yesterdayData.marketListingsNetworth,
                houseDelta: todayData.totalHouseScore - yesterdayData.totalHouseScore,
                abilityDelta: todayData.abilityScore - yesterdayData.abilityScore,
                totalDelta: todayData.totalAssets - yesterdayData.totalAssets,
                totalRatio: yesterdayData.totalAssets > 0 ?
                    (todayData.totalAssets - yesterdayData.totalAssets) / yesterdayData.totalAssets * 100 : 0
            };
        }

        getHistoryData(days = 30) {
            const roleData = this.getRoleData();
            const cutoff = Date.now() - (days * 24 * 3600 * 1000);

            const filtered = Object.entries(roleData)
                .filter(([_, data]) => data.timestamp > cutoff)
                .sort(([a], [b]) => new Date(a) - new Date(b));

            return {
                labels: filtered.map(([date]) => date),
                equippedNetworth: filtered.map(([_, data]) => data.equippedNetworth),
                inventoryNetworth: filtered.map(([_, data]) => data.inventoryNetworth),
                marketListingsNetworth: filtered.map(([_, data]) => data.marketListingsNetworth),
                totalHouseScore: filtered.map(([_, data]) => data.totalHouseScore),
                abilityScore: filtered.map(([_, data]) => data.abilityScore),
                currentAssets: filtered.map(([_, data]) => data.currentAssets),
                nonCurrentAssets: filtered.map(([_, data]) => data.nonCurrentAssets),
                totalAssets: filtered.map(([_, data]) => data.totalAssets)
            };
        }

        getHistoryEntriesSorted() {
            const roleData = this.getRoleData();
            return Object.entries(roleData)
                .filter(([_, data]) => data.totalAssets !== undefined)
                .map(([date, data]) => [date, data.totalAssets])
                .sort(([a], [b]) => new Date(a) - new Date(b));
        }

        getAllRoles() {
            return Object.keys(this.data);
        }

        removeRole(roleId) {
            delete this.data[roleId];
            this.saveToStorage();
        }

        getTotalDataForEP() {
            const result = {};

            Object.entries(this.data).forEach(([role, roleData]) => {
                result[role] = {};
                Object.entries(roleData).forEach(([date, data]) => {
                    result[role][date] = data.totalAssets;
                });
            });

            return result;
        }

        getBreakdownDataForEP() {
            const result = {};

            Object.entries(this.data).forEach(([role, roleData]) => {
                result[role] = {};
                Object.entries(roleData).forEach(([date, data]) => {
                    result[role][date] = {
                        equip: data.equippedNetworth || 0,
                        inventory: data.inventoryNetworth || 0,
                        orders: data.marketListingsNetworth || 0,
                        house: data.totalHouseScore || 0,
                        skill: data.abilityScore || 0
                    };
                });
            });

            return result;
        }

        debugCurrentData() {
            console.log('=== DailyAssets Plus 当前数据 ===');
            console.log('存储键:', this.storageKey);
            console.log('当前角色:', this.currentRole);
            console.log('所有角色:', Object.keys(this.data));

            Object.entries(this.data).forEach(([role, roleData]) => {
                console.log(`\n角色: ${role}`);
                const dates = Object.keys(roleData).sort();
                console.log(`记录天数: ${dates.length}`);

                if (dates.length > 0) {
                    const latest = dates[dates.length - 1];
                    const data = roleData[latest];
                    console.log(`最新记录 (${latest}):`, {
                        装备: data.equippedNetworth,
                        库存: data.inventoryNetworth,
                        订单: data.marketListingsNetworth,
                        房子: data.totalHouseScore,
                        技能: data.abilityScore,
                        总资产: data.totalAssets
                    });
                }
            });

            console.log('\n=== EP 格式数据预览 ===');
            console.log('kbd_calc_data:', this.getTotalDataForEP());
            console.log('kbd_calc_breakdown_data:', this.getBreakdownDataForEP());
        }

        debugEPData() {
            console.log('=== EP 存储中的数据 ===');
            const totalData = safeJsonParse(localStorage.getItem(EP_STORAGE_KEYS.totalData), {});
            const breakdownData = safeJsonParse(localStorage.getItem(EP_STORAGE_KEYS.breakdownData), {});

            console.log('kbd_calc_data:', totalData);
            console.log('kbd_calc_breakdown_data:', breakdownData);

            // 检查当前角色是否有数据
            if (this.currentRole) {
                console.log(`当前角色 ${this.currentRole} 的 EP 数据:`);
                console.log('总资产:', totalData[this.currentRole]);
                console.log('分项资产:', breakdownData[this.currentRole]);
            }
        }
    }

    /* =========================
       统计指标计算函数
    ========================= */
    const computeDeltas = (sortedEntries) => {
        const diff = [];
        for (let i = 1; i < sortedEntries.length; i++) {
            const prev = sortedEntries[i - 1][1];
            const curr = sortedEntries[i][1];
            diff.push({
                date: sortedEntries[i][0],
                value: curr - prev,
                growthPct: prev ? ((curr - prev) / prev) * 100 : 0,
            });
        }
        return diff;
    };

    const computeStreaks = (differences) => {
        let bestGain = 0;
        let worstLoss = 0;
        let bestDay = null;
        let worstDay = null;
        let winStreak = 0;
        let loseStreak = 0;
        let currentWin = 0;
        let currentLose = 0;

        differences.forEach((d) => {
            if (d.value >= 0) {
                currentWin += 1;
                currentLose = 0;
                if (d.value > bestGain) {
                    bestGain = d.value;
                    bestDay = d.date;
                }
            } else {
                currentLose += 1;
                currentWin = 0;
                if (d.value < worstLoss) {
                    worstLoss = d.value;
                    worstDay = d.date;
                }
            }
            winStreak = Math.max(winStreak, currentWin);
            loseStreak = Math.max(loseStreak, currentLose);
        });

        return { bestGain, bestDay, worstLoss, worstDay, winStreak, loseStreak };
    };

    const predictDoublingTime = (differences, currentValue, windowDays = 7) => {
        if (!currentValue || differences.length === 0) return null;
        const recent = differences.slice(-windowDays);
        if (!recent.length) return null;
        const avgGrowth = recent.reduce((sum, d) => sum + d.value, 0) / recent.length;
        if (avgGrowth <= 0) return null;
        return Math.ceil((currentValue) / avgGrowth);
    };

    const predictTargetDate = (differences, currentValue, targetValue) => {
        if (!currentValue || currentValue >= targetValue) {
            return { days: 0, targetValue };
        }
        const recent = differences.slice(-7);
        const avgGrowth = recent.reduce((sum, d) => sum + d.value, 0) / (recent.length || 1);
        if (avgGrowth <= 0) return null;
        const remaining = targetValue - currentValue;
        return { days: Math.ceil(remaining / avgGrowth), targetValue };
    };

    const nextRoundNumber = (value) => {
        if (!value) return 0;
        const magnitude = Math.pow(10, Math.max(3, Math.floor(Math.log10(value))));
        return Math.ceil(value / magnitude) * magnitude;
    };

    const computeTotalMetricsFromEntries = (sortedEntries, unitMode = 'auto') => {
        const latestRecordDate = sortedEntries.length ? sortedEntries[sortedEntries.length - 1][0] : '-';
        const valueToPersist = sortedEntries.length ? (sortedEntries[sortedEntries.length - 1][1] || 0) : 0;

        const differences = computeDeltas(sortedEntries);
        const todayDelta = differences.length ? differences[differences.length - 1] : null;
        const growthPct = todayDelta ? (todayDelta.growthPct || 0) : 0;
        const hourlyRate = todayDelta ? (todayDelta.value / 24) : 0;

        const last7 = differences.slice(-7);
        const avgGrowthPct = last7.length ? last7.reduce((sum, d) => sum + (d.growthPct || 0), 0) / last7.length : 0;
        const avgGrowthValue = last7.length ? last7.reduce((sum, d) => sum + (d.value || 0), 0) / last7.length : 0;

        const streaks = computeStreaks(differences);

        const doublingDays = predictDoublingTime(differences, valueToPersist);
        const targetValue = nextRoundNumber(valueToPersist * 1.05);
        const targetPrediction = predictTargetDate(differences, valueToPersist, targetValue);

        let lastDoubleDate = null;
        let lastDoubleDays = null;
        let lastDoubleValue = null;
        if (valueToPersist > 0 && sortedEntries.length) {
            const halfValue = valueToPersist / 2;
            const milestoneEntry = sortedEntries.find(([, v]) => Number.isFinite(v) && v >= halfValue);
            if (milestoneEntry) {
                lastDoubleDate = milestoneEntry[0];
                lastDoubleValue = milestoneEntry[1];
                const diffMs = Date.now() - new Date(lastDoubleDate).getTime();
                lastDoubleDays = Math.max(0, Math.floor(diffMs / 86400000));
            }
        }

        return {
            latestRecordDate,
            valueToPersist,
            differences,
            todayDelta,
            growthPct,
            hourlyRate,
            avgGrowthPct,
            avgGrowthValue,
            streaks,
            lastDoubleDate,
            lastDoubleDays,
            lastDoubleValue,
            doublingDays,
            targetPrediction,
            unitMode
        };
    };

    const buildMetricCards = (metrics, unitMode = 'auto') => {
        if (!metrics) return '';
        const cards = metrics.map((metric) => `
            <div class="ep-metric-card">
                <h4>${metric.title}</h4>
                <strong>${metric.value}</strong>
                ${metric.desc ? `<span>${metric.desc}</span>` : ''}
            </div>
        `).join('');
        return `<div class="ep-metrics-grid">${cards}</div>`;
    };

    /* =========================
       设置系统
    ========================= */
    function getChartOptions() {
        const defaults = {
            viewMode: 'summary',
            summaryShowCurrent: true,
            summaryShowNonCurrent: true,
            detailedShowEquipped: true,
            detailedShowInventory: true,
            detailedShowMarketListings: true,
            detailedShowHouse: true,
            detailedShowAbility: true,
            showTotal: true,
            daysToShow: 30,
            showTimeRangeSettings: true,
            visibleTimeRanges: [3, 7, 30, 60, 90, 180],
            unitMode: 'auto',
            showMetricsPanel: true
        };
        const saved = GM_getValue('chartOptions', defaults);
        return {...defaults, ...saved};
    }

    function saveChartOptions(options) {
        GM_setValue('chartOptions', options);
    }

    /* =========================
       导入导出管理器(完全兼容 Everyday Profit Pro 格式)
    ========================= */
    class ImportExportManager {
        constructor(store) {
            this.store = store;
            this.importData = null;
        }

        // 导出数据(完全兼容 Everyday Profit Pro 格式)
        exportData(includeSettings = true) {
            const breakdownData = this.store.getBreakdownDataForEP();

            // 确保分项数据只有5个字段
            Object.keys(breakdownData).forEach(role => {
                Object.keys(breakdownData[role] || {}).forEach(date => {
                    const entry = breakdownData[role][date];
                    breakdownData[role][date] = {
                        equip: entry.equip || 0,
                        inventory: entry.inventory || 0,
                        orders: entry.orders || 0,
                        house: entry.house || 0,
                        skill: entry.skill || 0
                    };
                });
            });

            const exportObj = {
                __everyday_profit_backup__: true,
                schema: 3,
                exportedAt: new Date().toISOString(),
                payload: {
                    kbd_calc_data: this.store.getTotalDataForEP(),
                    kbd_calc_breakdown_data: breakdownData,
                    kbd_calc_tags: {},
                    kbd_calc_tag_prefs: {},
                    kbd_calc_tag_panel: {},
                    kbd_calc_data_panel: {},
                    ep_achievements_data: {}
                }
            };

            if (includeSettings) {
                exportObj.settings = {
                    ep_tag_colors: null,
                    ep_window_size: null,
                    ep_chart_settings: GM_getValue('chartOptions', null),
                    ep_heatmap_style: null,
                    ep_glass_heart_mode: null,
                    ep_theme_mode: null,
                    ep_light_bg: null
                };
            }

            return exportObj;
        }

        exportToJson(includeSettings = true) {
            const exportObj = this.exportData(includeSettings);
            return JSON.stringify(exportObj, null, 2);
        }

        downloadExport(includeSettings = true) {
            const jsonStr = this.exportToJson(includeSettings);
            const blob = new Blob([jsonStr], { type: 'application/json' });
            const url = URL.createObjectURL(blob);

            const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
            const filename = `milkyway_assets_backup_${timestamp}.json`;

            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);

            this.showNotification(`${t('exportSuccess')} ${filename}`, 'success');
        }

        validateImportData(data) {
            if (!data || typeof data !== 'object') {
                throw new Error(t('invalidFormat'));
            }

            if (data.__everyday_profit_backup__ !== true) {
                throw new Error(t('unrecognizedVersion'));
            }

            if (!data.payload || typeof data.payload !== 'object') {
                throw new Error(t('missingAssetData'));
            }

            if (!data.payload.kbd_calc_data || typeof data.payload.kbd_calc_data !== 'object') {
                throw new Error(t('missingAssetData'));
            }

            return true;
        }

        extractFromEverydayProfit(data) {
            const extracted = {
                assetData: {}
            };

            if (data.payload && data.payload.kbd_calc_data) {
                Object.entries(data.payload.kbd_calc_data).forEach(([role, roleData]) => {
                    if (!extracted.assetData[role]) {
                        extracted.assetData[role] = {};
                    }
                    Object.entries(roleData).forEach(([date, totalValue]) => {
                        extracted.assetData[role][date] = {
                            equippedNetworth: 0,
                            inventoryNetworth: 0,
                            marketListingsNetworth: 0,
                            totalHouseScore: 0,
                            abilityScore: 0,
                            currentAssets: totalValue,
                            nonCurrentAssets: 0,
                            totalAssets: totalValue,
                            timestamp: new Date(date).getTime()
                        };
                    });
                });
            }

            if (data.payload && data.payload.kbd_calc_breakdown_data) {
                Object.entries(data.payload.kbd_calc_breakdown_data).forEach(([role, roleData]) => {
                    if (!extracted.assetData[role]) {
                        extracted.assetData[role] = {};
                    }
                    Object.entries(roleData).forEach(([date, values]) => {
                        if (!extracted.assetData[role][date]) {
                            extracted.assetData[role][date] = {
                                timestamp: new Date(date).getTime()
                            };
                        }
                        const equip = values.equip || 0;
                        const inventory = values.inventory || 0;
                        const orders = values.orders || 0;
                        const house = values.house || 0;
                        const skill = values.skill || 0;
                        const total = equip + inventory + orders + house + skill;

                        extracted.assetData[role][date] = {
                            ...extracted.assetData[role][date],
                            equippedNetworth: equip,
                            inventoryNetworth: inventory,
                            marketListingsNetworth: orders,
                            totalHouseScore: house,
                            abilityScore: skill,
                            currentAssets: equip + inventory + orders,
                            nonCurrentAssets: house + skill,
                            totalAssets: total
                        };
                    });
                });
            }

            return extracted;
        }

        previewImport(jsonStr) {
            try {
                const data = JSON.parse(jsonStr);
                this.validateImportData(data);

                const extracted = this.extractFromEverydayProfit(data);
                const assetData = extracted.assetData;
                const roleCount = Object.keys(assetData).length;

                let totalAssetRecords = 0;
                Object.values(assetData).forEach(roleData => {
                    totalAssetRecords += roleData ? Object.keys(roleData).length : 0;
                });

                let previewHtml = `
                    <div class="import-preview show">
                        <div class="import-preview-title">${t('preview')}</div>
                        <div class="import-preview-content">
                            <p>📄 ${t('version')}: Everyday Profit Pro</p>
                            <p>📅 ${t('exportTime')}: ${new Date(data.exportedAt).toLocaleString()}</p>
                            <p>👥 ${t('roleCount')}: ${roleCount}</p>
                            <p>💰 ${t('totalRecords')}: ${totalAssetRecords}</p>
                `;

                previewHtml += `<p>📋 ${t('containsSettings')}: ${data.settings ? t('yes') : t('no')}</p>`;

                if (roleCount > 0) {
                    previewHtml += `<p>📈 ${t('roleStats')}</p><ul>`;
                    Object.entries(assetData).forEach(([role, roleData]) => {
                        const assetCount = roleData ? Object.keys(roleData).length : 0;
                        previewHtml += `<li>${role}: ${assetCount}${t('records')}</li>`;
                    });
                    previewHtml += '</ul>';
                }

                previewHtml += `
                        </div>
                        <div class="import-preview-actions">
                            <button class="confirm-import-btn">${t('confirmImport')}</button>
                            <button class="cancel-import-btn">${t('cancel')}</button>
                        </div>
                    </div>
                `;

                this.importData = { data: extracted };
                return previewHtml;
            } catch (error) {
                throw new Error(`${t('previewFailed')}: ${error.message}`);
            }
        }

        executeImport(options = { merge: true }) {
            if (!this.importData) {
                throw new Error(t('noImportData'));
            }

            const { data: extracted } = this.importData;

            // 合并或替换本地数据
            if (options.merge) {
                Object.entries(extracted.assetData).forEach(([role, roleData]) => {
                    if (!this.store.data[role]) {
                        this.store.data[role] = {};
                    }
                    Object.assign(this.store.data[role], roleData);
                });
            } else {
                this.store.data = extracted.assetData;
            }

            // 保存到本地存储
            this.store.saveToStorage();

            // 重要:直接写入 Everyday Profit Pro 的存储键
            // 从合并后的数据重建 EP 格式
            const epTotalData = {};
            const epBreakdownData = {};

            Object.entries(this.store.data).forEach(([role, roleData]) => {
                epTotalData[role] = {};
                epBreakdownData[role] = {};

                Object.entries(roleData).forEach(([date, data]) => {
                    // 写入总资产
                    epTotalData[role][date] = data.totalAssets;

                    // 写入分项资产
                    epBreakdownData[role][date] = {
                        equip: data.equippedNetworth || 0,
                        inventory: data.inventoryNetworth || 0,
                        orders: data.marketListingsNetworth || 0,
                        house: data.totalHouseScore || 0,
                        skill: data.abilityScore || 0
                    };
                });
            });

            // 保存到 localStorage
            localStorage.setItem(EP_STORAGE_KEYS.totalData, JSON.stringify(epTotalData));
            localStorage.setItem(EP_STORAGE_KEYS.breakdownData, JSON.stringify(epBreakdownData));

            console.log('[DailyAssets] 导入完成,已写入 EP 存储:', {
                total: epTotalData,
                breakdown: epBreakdownData
            });

            this.importData = null;

            this.showNotification('✅ 导入成功!正在刷新页面...', 'success');
            setTimeout(() => location.reload(), 1500);
        }

        showNotification(message, type = 'info') {
            const notification = document.createElement('div');
            notification.className = `notification ${type}`;
            notification.textContent = message;
            document.body.appendChild(notification);

            setTimeout(() => {
                notification.remove();
            }, 3000);
        }

        debugCurrentData() {
            this.store.debugCurrentData();
            this.store.debugEPData();
        }
    }

    function createFileInput() {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = '.json,application/json';
        input.className = 'file-input-hidden';
        input.id = 'importFileInput';
        document.body.appendChild(input);
        return input;
    }

    function addImportExportToSettings(settingsModal, importExportManager) {
        const settingsBody = document.getElementById('assetSettingsBody');

        const importExportSection = document.createElement('div');
        importExportSection.className = 'import-export-section';
        importExportSection.innerHTML = `
            <span class="settings-title">${t('importExport')}</span>
            <div class="import-export-buttons">
                <button class="import-export-btn export-all" id="exportFullBtn">${t('exportAll')}</button>
                <button class="import-export-btn" id="exportDataBtn">${t('exportData')}</button>
                <button class="import-export-btn import" id="importBtn">${t('importData')}</button>
                <button class="import-export-btn" id="debugDataBtn" style="background:#9C27B0;">🔍 调试</button>
            </div>
            <div id="importPreviewContainer"></div>
        `;

        settingsBody.appendChild(importExportSection);

        const fileInput = createFileInput();

        document.getElementById('exportFullBtn').addEventListener('click', () => {
            try {
                importExportManager.downloadExport(true);
            } catch (error) {
                importExportManager.showNotification(`${t('exportFailed')}: ${error.message}`, 'error');
            }
        });

        document.getElementById('exportDataBtn').addEventListener('click', () => {
            try {
                importExportManager.downloadExport(false);
            } catch (error) {
                importExportManager.showNotification(`${t('exportFailed')}: ${error.message}`, 'error');
            }
        });

        document.getElementById('importBtn').addEventListener('click', () => {
            fileInput.click();
        });

        document.getElementById('debugDataBtn').addEventListener('click', () => {
            importExportManager.debugCurrentData();
            importExportManager.showNotification('数据已输出到控制台 (F12)', 'info');
        });

        fileInput.addEventListener('change', (event) => {
            const file = event.target.files[0];
            if (!file) return;

            const reader = new FileReader();
            reader.onload = (e) => {
                try {
                    const previewHtml = importExportManager.previewImport(e.target.result);
                    const previewContainer = document.getElementById('importPreviewContainer');
                    previewContainer.innerHTML = previewHtml;

                    const confirmBtn = previewContainer.querySelector('.confirm-import-btn');
                    const cancelBtn = previewContainer.querySelector('.cancel-import-btn');

                    if (confirmBtn) {
                        confirmBtn.addEventListener('click', () => {
                            try {
                                importExportManager.executeImport({ merge: true });
                            } catch (error) {
                                importExportManager.showNotification(`${t('importFailed')}: ${error.message}`, 'error');
                            }
                        });
                    }

                    if (cancelBtn) {
                        cancelBtn.addEventListener('click', () => {
                            previewContainer.innerHTML = '';
                            importExportManager.importData = null;
                            fileInput.value = '';
                        });
                    }
                } catch (error) {
                    importExportManager.showNotification(error.message, 'error');
                    fileInput.value = '';
                }
            };
            reader.readAsText(file);
        });
    }

    GM_addStyle(`
        /* 基础样式 */
        .asset-delta-display {
            text-align: left;
            color: #fff;
            font-size: 16px;
            margin: 0px 0;
        }
        .asset-delta-label {
            font-weight: bold;
            margin-right: 5px;
        }
        #showHistoryIcon, #showMetricsIcon, #languageToggle {
            cursor: pointer;
            margin-left: 8px;
            font-size: 16px;
            display: inline-block;
            margin-top: 0px;
            opacity: 0.8;
            transition: opacity 0.2s;
        }
        #showHistoryIcon:hover, #showMetricsIcon:hover, #languageToggle:hover {
            opacity: 1;
        }
        #languageToggle {
            color: #FFD700;
        }
        #settingsToggle {
            cursor: pointer;
            margin-left: 8px;
            font-size: 16px;
            color: #2196F3;
            opacity: 0.8;
            transition: opacity 0.2s;
        }
        #settingsToggle:hover {
            opacity: 1;
        }
        .positive-delta {
            color: #4CAF50;
            font-weight: bold;
        }
        .negative-delta {
            color: #F44336;
            font-weight: bold;
        }
        .neutral-delta {
            color: #9E9E9E;
            font-weight: bold;
        }

        /* 统计指标面板 */
        .ep-metrics-grid {
            display: grid;
            grid-template-columns: repeat(4, minmax(0, 1fr));
            gap: 10px;
            padding: 10px 15px;
            background: #111b2b;
            border-top: 1px solid rgba(255,255,255,0.05);
            border-bottom: 1px solid rgba(255,255,255,0.05);
        }
        @media (max-width: 720px) {
            .ep-metrics-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
        }
        @media (max-width: 420px) {
            .ep-metrics-grid { grid-template-columns: 1fr; }
        }

        .ep-metric-card {
            background: rgba(255,255,255,0.06);
            border: 1px solid rgba(255,255,255,0.08);
            border-radius: 8px;
            padding: 8px 10px;
            display: flex;
            flex-direction: column;
            gap: 4px;
            min-width: 0;
        }
        .ep-metric-card h4 {
            font-size: 12px;
            font-weight: normal;
            color: #9fb4d1;
            margin: 0;
        }
        .ep-metric-card strong {
            font-size: 18px;
            color: #f7fafc;
            word-break: break-word;
        }
        .ep-metric-card span {
            font-size: 12px;
            color: #7f8ca3;
            word-break: break-word;
        }

        /* 额外信息行 */
        .ep-delta-extra {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-top: 6px;
            font-size: 14px;
            color: #cfd8e3;
        }
        .ep-delta-extra span {
            background: rgba(255, 255, 255, 0.08);
            border-radius: 4px;
            padding: 3px 8px;
        }

        /* 弹窗样式 */
        #deltaNetworthChartModal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 1200px;
            max-width: 95vw;
            background: #1e1e1e;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.6);
            z-index: 10000;
            display: none;
            flex-direction: column;
            color: #f5f5f5;
            border: 1px solid rgba(255,255,255,0.08);
        }
        #deltaNetworthChartModal.dragging {
            cursor: grabbing;
        }
        #deltaNetworthChartHeader {
            padding: 10px 15px;
            background: #333;
            color: white;
            font-weight: bold;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
            user-select: none;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
        }
        #netWorthChartBody {
            padding: 15px;
            background: #0b1522;
            border-bottom-left-radius: 8px;
            border-bottom-right-radius: 8px;
            border: 1px solid rgba(255,255,255,0.05);
        }
        #netWorthChart {
            width: 100%;
            height: 400px;
            background: radial-gradient(circle at top, rgba(0,198,255,0.08), rgba(2,12,24,0.95));
            border-radius: 6px;
        }

        /* 设置弹窗样式 */
        #assetSettingsModal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 600px;
            max-width: 95vw;
            background: #1e1e1e;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.6);
            z-index: 10001;
            display: none;
            flex-direction: column;
        }
        #assetSettingsModal.dragging {
            cursor: grabbing;
        }
        #assetSettingsHeader {
            padding: 10px 15px;
            background: #333;
            color: white;
            font-weight: bold;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: default;
            user-select: none;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
        }
        #assetSettingsBody {
            padding: 15px;
            max-height: 70vh;
            overflow-y: auto;
        }

        /* 模态遮罩 */
        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 9999;
            display: none;
        }

        /* 关闭按钮 */
        .close-btn {
            cursor: pointer;
            font-size: 18px;
            color: #fff;
        }
        .close-btn:hover {
            color: #f44336;
        }

        /* 视图切换 */
        .view-toggle {
            display: flex;
            background: #333;
            border-radius: 4px;
            padding: 2px;
            margin-bottom: 15px;
        }
        .view-option {
            flex: 1;
            text-align: center;
            padding: 8px;
            cursor: pointer;
            border-radius: 3px;
            font-weight: bold;
            transition: all 0.3s ease;
        }
        .view-option.active {
            background: #4CAF50;
            color: white;
        }
        .view-option:not(.active) {
            background: transparent;
            color: #ccc;
        }
        .view-option:not(.active):hover {
            background: #444;
        }

        /* 设置区域 */
        .settings-section {
            background: #2a2a2a;
            padding: 15px;
            margin: 2px 0;
            border-radius: 4px;
        }
        .settings-title {
            color: #fff;
            font-weight: bold;
            font-size: 16px;
            margin-bottom: 5px;
            display: block;
        }
        .settings-group {
            color: #fff;
            margin-bottom: 15px;
        }
        .chart-option {
            margin: 5px 0;
            display: flex;
            align-items: center;
        }
        .chart-option input {
            margin-right: 8px;
        }
        .chart-option label {
            cursor: pointer;
            color: white;
            flex-grow: 1;
        }

        /* 时间范围按钮 */
        .time-range-btn {
            padding: 5px 10px;
            background: rgba(0,0,0,.3);
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-right: 5px;
            margin-bottom: 5px;
        }
        .time-range-btn:hover {
            background: #555;
        }
        .time-range-btn.active {
            background: rgb(25, 118, 210);
            font-weight: bold;
        }
        .time-range-section {
            margin: 15px 0;
        }

        /* 图表选项容器 */
        #chartOptionsContainer {
            padding: 4px;
            background: #111b2b;
            border-bottom: 0px solid #333;
        }
        #timeRangeOptions {
            margin-top: 10px;
            color: #fff;
        }
        #timeRangeOptions.hidden {
            display: none;
        }
        .time-range-buttons {
            margin-top: 10px;
        }

        /* 单位设置 */
        .unit-toggle {
            display: flex;
            background: #333;
            border-radius: 4px;
            padding: 2px;
            margin-top: 8px;
        }
        .unit-option {
            flex: 1;
            text-align: center;
            padding: 6px;
            cursor: pointer;
            border-radius: 3px;
            font-weight: bold;
            font-size: 14px;
            transition: all 0.3s ease;
        }
        .unit-option.active {
            background: #2196F3;
            color: white;
        }
        .unit-option:not(.active) {
            background: transparent;
            color: #ccc;
        }
        .unit-option:not(.active):hover {
            background: #444;
        }

        /* 导入导出样式 */
        .import-export-section {
            background: #2a2a2a;
            padding: 15px;
            margin: 2px 0;
            border-radius: 4px;
        }

        .import-export-buttons {
            display: flex;
            gap: 10px;
            margin-top: 10px;
            flex-wrap: wrap;
        }

        .import-export-btn {
            padding: 8px 15px;
            background: #2196F3;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background 0.2s;
            flex: 1;
            min-width: 100px;
        }

        .import-export-btn:hover {
            background: #1976D2;
        }

        .import-export-btn.export-all {
            background: #4CAF50;
        }

        .import-export-btn.export-all:hover {
            background: #45a049;
        }

        .import-export-btn.import {
            background: #FF9800;
        }

        .import-export-btn.import:hover {
            background: #F57C00;
        }

        .file-input-hidden {
            display: none;
        }

        .import-preview {
            margin-top: 15px;
            padding: 10px;
            background: #333;
            border-radius: 4px;
            display: none;
        }

        .import-preview.show {
            display: block;
        }

        .import-preview-title {
            color: #fff;
            font-weight: bold;
            margin-bottom: 10px;
        }

        .import-preview-content {
            max-height: 200px;
            overflow-y: auto;
            color: #ccc;
            font-size: 12px;
        }

        .import-preview-actions {
            display: flex;
            gap: 10px;
            margin-top: 10px;
        }

        .confirm-import-btn {
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 5px 10px;
            cursor: pointer;
        }

        .cancel-import-btn {
            background: #f44336;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 5px 10px;
            cursor: pointer;
        }

        .notification {
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 10px 20px;
            background: #333;
            color: white;
            border-radius: 4px;
            z-index: 10002;
            animation: slideIn 0.3s ease;
        }

        .notification.success {
            background: #4CAF50;
        }

        .notification.error {
            background: #f44336;
        }

        .notification.info {
            background: #2196F3;
        }

        @keyframes slideIn {
            from {
                transform: translateX(100%);
                opacity: 0;
            }
            to {
                transform: translateX(0);
                opacity: 1;
            }
        }
    `);

    /* =========================
       主逻辑
    ========================= */
    window.kbd_calculateTotalNetworth = function kbd_calculateTotalNetworth(
        equippedNetworth,
        inventoryNetworth,
        marketListingsNetworth,
        totalHouseScore,
        abilityScore,
        dom
    ) {
        const detectRoleId = () => {
            const candidates = [
                document.querySelector('.CharacterName_name__1amXp span'),
                document.querySelector('[class*="CharacterName_name"] span'),
                document.querySelector('[data-testid="character-name"]'),
            ];
            const text = (candidates.find(Boolean)?.textContent || '').replace(/\s+/g, ' ').trim();
            return text || 'default';
        };

        const roleId = detectRoleId();
        console.log('[DailyAssets] 检测到角色ID:', roleId);

        const store = new AssetDataStore();
        store.setRole(roleId);

        // 确保调试对象正确初始化
        window.__dailyAssetsDebug = {
            store: store,
            debug: () => store.debugCurrentData(),
            debugEP: () => store.debugEPData(),
            checkEP: () => {
                console.log('=== 检查 EP 存储 ===');
                const total = localStorage.getItem('kbd_calc_data');
                const breakdown = localStorage.getItem('kbd_calc_breakdown_data');
                console.log('kbd_calc_data 是否存在:', !!total);
                console.log('kbd_calc_breakdown_data 是否存在:', !!breakdown);
                if (total) {
                    console.log('kbd_calc_data 内容:', JSON.parse(total));
                }
                if (breakdown) {
                    console.log('kbd_calc_breakdown_data 内容:', JSON.parse(breakdown));
                }
            }
        };

        console.log('[DailyAssets] 调试对象已初始化:', window.__dailyAssetsDebug);

        let chart = null;
        let currentModal = null;
        let metricsPanelVisible = getRoleBoolPref(STORAGE_KEYS.metricsPanel, roleId, true);

        function createOverlay() {
            const overlay = document.createElement('div');
            overlay.className = 'modal-overlay';
            overlay.id = 'modalOverlay';
            overlay.addEventListener('click', (e) => {
                if (e.target === overlay) {
                    hideAllModals();
                }
            });
            document.body.appendChild(overlay);
            return overlay;
        }

        function showOverlay() {
            const overlay = document.getElementById('modalOverlay') || createOverlay();
            overlay.style.display = 'block';
        }

        function hideOverlay() {
            const overlay = document.getElementById('modalOverlay');
            if (overlay) {
                overlay.style.display = 'none';
            }
        }

        function hideAllModals() {
            document.querySelectorAll('#deltaNetworthChartModal, #assetSettingsModal').forEach(modal => {
                modal.style.display = 'none';
            });
            hideOverlay();
            currentModal = null;
        }

        function setupDrag(modal) {
            let isDragging = false;
            let startX, startY, initialLeft, initialTop;

            const header = modal.querySelector('#deltaNetworthChartHeader') || modal.querySelector('#assetSettingsHeader');

            header.addEventListener('mousedown', (e) => {
                if (e.target.className === 'close-btn') return;

                isDragging = true;
                startX = e.clientX;
                startY = e.clientY;
                const rect = modal.getBoundingClientRect();
                initialLeft = rect.left;
                initialTop = rect.top;
                modal.classList.add('dragging');
                e.preventDefault();
            });

            document.addEventListener('mousemove', (e) => {
                if (isDragging) {
                    const dx = e.clientX - startX;
                    const dy = e.clientY - startY;
                    modal.style.left = `${initialLeft + dx}px`;
                    modal.style.top = `${initialTop + dy}px`;
                    modal.style.transform = 'none';
                }
            });

            document.addEventListener('mouseup', () => {
                isDragging = false;
                modal.classList.remove('dragging');
            });
        }

        function renderMetricsPanel() {
            const entries = store.getHistoryEntriesSorted();
            if (entries.length < 2) return '';

            const options = getChartOptions();
            const mtxAll = computeTotalMetricsFromEntries(entries, options.unitMode);
            const lastUpdateIso = getRoleLastUpdate(roleId);
            const lastUpdateStr = lastUpdateIso ? formatIsoToLocalDateTime(lastUpdateIso) : (mtxAll.latestRecordDate || '-');

            const diffs = Array.isArray(mtxAll.differences) ? mtxAll.differences : [];
            const last7Diffs = diffs.slice(-7);
            const last30Diffs = diffs.slice(-30);

            const winCount7 = last7Diffs.filter((d) => d && Number.isFinite(d.value) && d.value > 0).length;
            const loseCount7 = last7Diffs.filter((d) => d && Number.isFinite(d.value) && d.value < 0).length;
            const flatCount7 = last7Diffs.filter((d) => d && Number.isFinite(d.value) && d.value === 0).length;
            const rate7 = last7Diffs.length ? (winCount7 / last7Diffs.length) * 100 : null;

            const avg30Value = last30Diffs.length ? last30Diffs.reduce((sum, d) => sum + (d.value || 0), 0) / last30Diffs.length : null;
            const avg30Pct = last30Diffs.length ? last30Diffs.reduce((sum, d) => sum + (d.growthPct || 0), 0) / last30Diffs.length : null;

            const bestWorstValue = `${formatSignedLargeNumber(mtxAll.streaks?.bestGain || 0, options.unitMode)} / ${formatSignedLargeNumber(mtxAll.streaks?.worstLoss || 0, options.unitMode)}`;
            const bestWorstDesc = `${mtxAll.streaks?.bestDay || '-'} | ${mtxAll.streaks?.worstDay || '-'}`;

            const predictBits = [];
            if (mtxAll.doublingDays) predictBits.push(`${t('doubling')}: ${mtxAll.doublingDays}${t('days')}`);
            if (mtxAll.targetPrediction) predictBits.push(`${t('target')}: ${mtxAll.targetPrediction.days}${t('days')}→${formatLargeNumber(mtxAll.targetPrediction.targetValue, options.unitMode)}`);

            const metrics = [
                { title: t('currentAssets'), value: formatLargeNumber(mtxAll.valueToPersist || 0, options.unitMode), desc: `${t('recentUpdate')}: ${lastUpdateStr}` },
                {
                    title: t('dailyChange'),
                    value: `${(mtxAll.growthPct >= 0 ? '+' : '')}${(mtxAll.growthPct || 0).toFixed(2)}%`,
                    desc: formatSignedLargeNumber(mtxAll.todayDelta?.value || 0, options.unitMode),
                },
                { title: t('hourlyRate'), value: formatLargeNumber(mtxAll.hourlyRate || 0, options.unitMode), desc: t('hourlyRateDesc') },
                {
                    title: t('avg7Growth'),
                    value: `${(mtxAll.avgGrowthPct >= 0 ? '+' : '')}${(mtxAll.avgGrowthPct || 0).toFixed(2)}% / ${formatSignedLargeNumber(mtxAll.avgGrowthValue || 0, options.unitMode)}`,
                    desc: t('avg7Desc'),
                },
                {
                    title: t('winRate7'),
                    value: rate7 === null ? '—' : `${winCount7}/${last7Diffs.length} (${rate7.toFixed(0)}%)`,
                    desc: `${t('profitLossFlat')}: ${winCount7}/${loseCount7}/${flatCount7}`,
                },
                {
                    title: t('avg30Daily'),
                    value: avg30Value === null ? '—' : `${(avg30Pct >= 0 ? '+' : '')}${(avg30Pct || 0).toFixed(2)}% / ${formatSignedLargeNumber(avg30Value || 0, options.unitMode)}`,
                    desc: predictBits.length ? `${predictBits.join(' | ')}` : t('avg7Desc'),
                },
                { title: t('bestWorstDay'), value: bestWorstValue, desc: bestWorstDesc },
                {
                    title: t('lastDouble'),
                    value: Number.isFinite(mtxAll.lastDoubleDays) ? `${mtxAll.lastDoubleDays} ${t('days')}` : t('noRecord'),
                    desc: mtxAll.lastDoubleDate ? `${mtxAll.lastDoubleDate}: ${formatLargeNumber(mtxAll.lastDoubleValue || 0, options.unitMode)}` : '—',
                },
            ];

            return buildMetricCards(metrics);
        }

        const updateDisplay = (isFirst = false) => {
            console.log('[DailyAssets] 更新数据:', {
                equipped: equippedNetworth,
                inventory: inventoryNetworth,
                market: marketListingsNetworth,
                house: totalHouseScore,
                ability: abilityScore
            });

            store.setTodayDetailedValues(
                equippedNetworth,
                inventoryNetworth,
                marketListingsNetworth,
                totalHouseScore,
                abilityScore
            );

            setRoleLastUpdate(roleId);

            const deltas = store.getTodayDeltas();
            const options = getChartOptions();
            const formattedTotalDelta = formatLargeNumber(deltas.totalDelta, options.unitMode);
            const totalDeltaClass = deltas.totalDelta > 0 ? 'positive-delta' :
                                  (deltas.totalDelta < 0 ? 'negative-delta' : 'neutral-delta');

            const entries = store.getHistoryEntriesSorted();
            const last7 = entries.slice(-8);
            let avg7 = 0;
            if (last7.length >= 2) {
                let s = 0;
                let c = 0;
                for (let i = 1; i < last7.length; i++) {
                    const d = last7[i][1] - last7[i - 1][1];
                    if (Number.isFinite(d)) { s += d; c += 1; }
                }
                avg7 = c ? s / c : 0;
            }

            const lastUpdateIso = getRoleLastUpdate(roleId);
            const lastUpdateStr = lastUpdateIso ? formatIsoToLocalDateTime(lastUpdateIso) : (entries.length ? entries[entries.length - 1][0] : '—');

            if (isFirst) {
                const metricsHTML = metricsPanelVisible ? renderMetricsPanel() : '';

                dom.insertAdjacentHTML('afterend', `
                    <div id="assetDeltaContainer" style="margin-top: 0px;">
                        <div class="asset-delta-display">
                            <span class="asset-delta-label">${t('assetsGrowth')}:</span>
                            <span class="${totalDeltaClass}">${formattedTotalDelta}</span>
                            <span id="showHistoryIcon" title="${t('historyIcon')}">📊</span>
                            <span id="showMetricsIcon" title="${metricsPanelVisible ? t('metricsIconHide') : t('metricsIconShow')}">📈</span>
                            <span id="languageToggle" title="中文/English">🌐</span>
                            <span id="settingsToggle" title="${t('settingsIcon')}">⚙️</span>
                        </div>
                        <div class="ep-delta-extra">
                            <span>${t('avg7Days')}: ${formatSignedLargeNumber(avg7, options.unitMode)}</span>
                            <span>${t('lastRecord')}: ${lastUpdateStr}</span>
                        </div>
                        ${metricsHTML}
                    </div>
                `);

                const chartModal = document.createElement('div');
                chartModal.id = 'deltaNetworthChartModal';
                chartModal.innerHTML = `
                    <div id="deltaNetworthChartHeader">
                        <span>${t('chartTitle')} (v${GM_info.script.version})</span>
                        <span class="close-btn" id="chartModalCloseBtn">❌</span>
                    </div>
                    <div id="chartOptionsContainer">
                        <div id="timeRangeOptions">
                            <span style="margin-right:10px;font-weight:bold;color:#fff;">${t('timeRange')}</span>
                        </div>
                    </div>
                    <div id="ep-metrics-container"></div>
                    <div id="netWorthChartBody">
                        <canvas id="netWorthChart"></canvas>
                    </div>
                `;
                document.body.appendChild(chartModal);

                const settingsModal = document.createElement('div');
                settingsModal.id = 'assetSettingsModal';
                settingsModal.innerHTML = `
                    <div id="assetSettingsHeader">
                        <span>${t('settingsTitle')} (v${GM_info.script.version})</span>
                        <span class="close-btn" id="settingsModalCloseBtn">❌</span>
                    </div>
                    <div id="assetSettingsBody">
                        <div class="settings-section">
                            <span class="settings-title">${t('viewMode')}</span>
                            <div class="view-toggle">
                                <div class="view-option active" data-view="summary">${t('summaryView')}</div>
                                <div class="view-option" data-view="detailed">${t('detailedView')}</div>
                            </div>
                        </div>

                        <div class="settings-section summary-view-options">
                            <span class="settings-title">${t('summaryOptions')}</span>
                            <div class="chart-option">
                                <input type="checkbox" id="summaryShowCurrent">
                                <label for="summaryShowCurrent">${t('showCurrent')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="summaryShowNonCurrent">
                                <label for="summaryShowNonCurrent">${t('showNonCurrent')}</label>
                            </div>
                        </div>

                        <div class="settings-section detailed-view-options hidden">
                            <span class="settings-title">${t('detailedOptions')}</span>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowEquipped">
                                <label for="detailedShowEquipped">${t('showEquipped')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowInventory">
                                <label for="detailedShowInventory">${t('showInventory')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowMarketListings">
                                <label for="detailedShowMarketListings">${t('showMarket')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowHouse">
                                <label for="detailedShowHouse">${t('showHouse')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowAbility">
                                <label for="detailedShowAbility">${t('showAbility')}</label>
                            </div>
                        </div>

                        <div class="settings-section">
                            <span class="settings-title">${t('generalOptions')}</span>
                            <div class="chart-option">
                                <input type="checkbox" id="showTotalOption">
                                <label for="showTotalOption">${t('showTotal')}</label>
                            </div>
                        </div>

                        <div class="settings-section">
                            <span class="settings-title">${t('unitSettings')}</span>
                            <div class="settings-group">
                                <span class="settings-label">${t('unitMode')}</span>
                                <div class="unit-toggle">
                                    <div class="unit-option active" data-unit="auto">${t('unitAuto')}</div>
                                    <div class="unit-option" data-unit="k">${t('unitK')}</div>
                                    <div class="unit-option" data-unit="m">${t('unitM')}</div>
                                    <div class="unit-option" data-unit="b">${t('unitB')}</div>
                                </div>
                            </div>
                        </div>

                        <div class="settings-section">
                            <span class="settings-title">${t('timeRangeSettings')}</span>
                            <div class="settings-group">
                                <label class="settings-label">
                                    <input type="checkbox" id="showTimeRangeToggle">
                                    ${t('showTimeRange')}
                                </label>
                            </div>

                            <div class="settings-group">
                                <span class="settings-label">${t('availableTimeRanges')}</span>
                                <div style="margin-top: 5px;">
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange3"> 3${t('days')}
                                    </label>
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange7"> 7${t('days')}
                                    </label>
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange30"> 30${t('days')}
                                    </label>
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange60"> 60${t('days')}
                                    </label>
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange90"> 90${t('days')}
                                    </label>
                                    <label style="color:#fff; display:inline-block;">
                                        <input type="checkbox" id="timeRange180"> 180${t('days')}
                                    </label>
                                </div>
                            </div>

                            <div class="settings-group">
                                <span class="settings-label">${t('currentButtons')}</span>
                                <div id="settingsTimeRangeButtons" class="time-range-buttons" style="margin-top: 10px;">
                                </div>
                            </div>
                        </div>
                    </div>
                `;
                document.body.appendChild(settingsModal);

                initSettings();

                const importExportManager = new ImportExportManager(store);
                addImportExportToSettings(settingsModal, importExportManager);

                window.__dailyAssetsImportExport = importExportManager;

                document.getElementById('showHistoryIcon').addEventListener('click', () => showChartModal());
                document.getElementById('settingsToggle').addEventListener('click', () => showSettingsModal());
                document.getElementById('showMetricsIcon').addEventListener('click', toggleMetricsPanel);
                document.getElementById('languageToggle').addEventListener('click', toggleLanguage);

                document.getElementById('chartModalCloseBtn').addEventListener('click', hideAllModals);
                document.getElementById('settingsModalCloseBtn').addEventListener('click', hideAllModals);

                document.querySelectorAll('.view-option').forEach(option => {
                    option.addEventListener('click', (e) => {
                        const view = e.target.dataset.view;
                        switchView(view);
                    });
                });

                document.querySelectorAll('.unit-option').forEach(option => {
                    option.addEventListener('click', (e) => {
                        const unit = e.target.dataset.unit;
                        switchUnit(unit);
                    });
                });

                document.getElementById('summaryShowCurrent').addEventListener('change', updateChartVisibility);
                document.getElementById('summaryShowNonCurrent').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowEquipped').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowInventory').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowMarketListings').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowHouse').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowAbility').addEventListener('change', updateChartVisibility);
                document.getElementById('showTotalOption').addEventListener('change', updateChartVisibility);

                document.getElementById('showTimeRangeToggle').addEventListener('change', toggleTimeRangeVisibility);
                document.getElementById('timeRange3').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange7').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange30').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange60').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange90').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange180').addEventListener('change', updateTimeRangeSettings);

                updateSettingsTimeRangeButtons();

                setupDrag(chartModal);
                setupDrag(settingsModal);

            } else {
                const container = document.getElementById('assetDeltaContainer');
                if (container) {
                    const metricsHTML = metricsPanelVisible ? renderMetricsPanel() : '';

                    container.innerHTML = `
                        <div class="asset-delta-display">
                            <span class="asset-delta-label">${t('assetsGrowth')}:</span>
                            <span class="${totalDeltaClass}">${formattedTotalDelta}</span>
                            <span id="showHistoryIcon" title="${t('historyIcon')}">📊</span>
                            <span id="showMetricsIcon" title="${metricsPanelVisible ? t('metricsIconHide') : t('metricsIconShow')}">📈</span>
                            <span id="languageToggle" title="中文/English">🌐</span>
                            <span id="settingsToggle" title="${t('settingsIcon')}">⚙️</span>
                        </div>
                        <div class="ep-delta-extra">
                            <span>${t('avg7Days')}: ${formatSignedLargeNumber(avg7, options.unitMode)}</span>
                            <span>${t('lastRecord')}: ${lastUpdateStr}</span>
                        </div>
                        ${metricsHTML}
                    `;

                    document.getElementById('showHistoryIcon').addEventListener('click', () => showChartModal());
                    document.getElementById('settingsToggle').addEventListener('click', () => showSettingsModal());
                    document.getElementById('showMetricsIcon').addEventListener('click', toggleMetricsPanel);
                    document.getElementById('languageToggle').addEventListener('click', toggleLanguage);
                }
            }
        };

        function toggleMetricsPanel() {
            metricsPanelVisible = !metricsPanelVisible;
            setRoleBoolPref(STORAGE_KEYS.metricsPanel, roleId, metricsPanelVisible);
            updateDisplay();
        }

        function switchView(viewMode) {
            const options = getChartOptions();
            options.viewMode = viewMode;
            saveChartOptions(options);

            document.querySelectorAll('.view-option').forEach(option => {
                if (option.dataset.view === viewMode) {
                    option.classList.add('active');
                } else {
                    option.classList.remove('active');
                }
            });

            const summaryOptions = document.querySelector('.summary-view-options');
            const detailedOptions = document.querySelector('.detailed-view-options');

            if (viewMode === 'summary') {
                summaryOptions.classList.remove('hidden');
                detailedOptions.classList.add('hidden');
            } else {
                summaryOptions.classList.add('hidden');
                detailedOptions.classList.remove('hidden');
            }

            if (chart) {
                recreateChart();
            }
        }

        function switchUnit(unitMode) {
            const options = getChartOptions();
            options.unitMode = unitMode;
            saveChartOptions(options);

            document.querySelectorAll('.unit-option').forEach(option => {
                if (option.dataset.unit === unitMode) {
                    option.classList.add('active');
                } else {
                    option.classList.remove('active');
                }
            });

            updateDisplay();
            if (chart) {
                chart.destroy();
                chart = null;
                initializeChart();
            }
        }

        function initSettings() {
            const options = getChartOptions();

            switchView(options.viewMode);

            document.getElementById('summaryShowCurrent').checked = options.summaryShowCurrent;
            document.getElementById('summaryShowNonCurrent').checked = options.summaryShowNonCurrent;

            document.getElementById('detailedShowEquipped').checked = options.detailedShowEquipped;
            document.getElementById('detailedShowInventory').checked = options.detailedShowInventory;
            document.getElementById('detailedShowMarketListings').checked = options.detailedShowMarketListings;
            document.getElementById('detailedShowHouse').checked = options.detailedShowHouse;
            document.getElementById('detailedShowAbility').checked = options.detailedShowAbility;

            document.getElementById('showTotalOption').checked = options.showTotal;

            switchUnit(options.unitMode);

            document.getElementById('showTimeRangeToggle').checked = options.showTimeRangeSettings;

            document.getElementById('timeRange3').checked = options.visibleTimeRanges.includes(3);
            document.getElementById('timeRange7').checked = options.visibleTimeRanges.includes(7);
            document.getElementById('timeRange30').checked = options.visibleTimeRanges.includes(30);
            document.getElementById('timeRange60').checked = options.visibleTimeRanges.includes(60);
            document.getElementById('timeRange90').checked = options.visibleTimeRanges.includes(90);
            document.getElementById('timeRange180').checked = options.visibleTimeRanges.includes(180);

            const timeRangeOptions = document.getElementById('timeRangeOptions');
            if (timeRangeOptions) {
                if (options.showTimeRangeSettings) {
                    timeRangeOptions.classList.remove('hidden');
                } else {
                    timeRangeOptions.classList.add('hidden');
                }
            }

            updateSettingsTimeRangeButtons();
        }

        function recreateChart() {
            if (chart) {
                chart.destroy();
                chart = null;
            }
            initializeChart();
        }

        function showChartModal() {
            hideAllModals();
            currentModal = document.getElementById('deltaNetworthChartModal');
            showOverlay();
            currentModal.style.display = 'flex';

            const metricsContainer = document.getElementById('ep-metrics-container');
            if (metricsContainer) {
                metricsContainer.innerHTML = renderMetricsPanel();
            }

            if (!window.Chart) {
                loadChartLibrary().then(() => {
                    initializeChart();
                    updateChartTimeRangeButtons();
                });
            } else if (!chart) {
                initializeChart();
                updateChartTimeRangeButtons();
            } else {
                updateChart();
                updateChartTimeRangeButtons();
            }
        }

        function showSettingsModal() {
            hideAllModals();
            currentModal = document.getElementById('assetSettingsModal');
            showOverlay();
            currentModal.style.display = 'flex';
            updateSettingsTimeRangeButtons();
        }

        function loadChartLibrary() {
            return new Promise((resolve) => {
                if (window.Chart) {
                    resolve();
                    return;
                }
                const script = document.createElement('script');
                script.src = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js';
                script.onload = resolve;
                document.head.appendChild(script);
            });
        }

        function initializeChart() {
            const options = getChartOptions();
            const historyData = store.getHistoryData(options.daysToShow);

            const ctx = document.getElementById('netWorthChart').getContext('2d');

            const datasets = [];

            if (options.viewMode === 'detailed') {
                datasets.push({
                    id: 'equipped',
                    label: t('equipmentValue'),
                    data: historyData.equippedNetworth,
                    borderColor: 'rgba(75, 192, 192, 1)',
                    backgroundColor: 'rgba(75, 192, 192, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowEquipped
                });
                datasets.push({
                    id: 'inventory',
                    label: t('inventoryValue'),
                    data: historyData.inventoryNetworth,
                    borderColor: 'rgba(255, 159, 64, 1)',
                    backgroundColor: 'rgba(255, 159, 64, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowInventory
                });
                datasets.push({
                    id: 'market',
                    label: t('marketValue'),
                    data: historyData.marketListingsNetworth,
                    borderColor: 'rgba(153, 102, 255, 1)',
                    backgroundColor: 'rgba(153, 102, 255, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowMarketListings
                });
                datasets.push({
                    id: 'house',
                    label: t('houseValue'),
                    data: historyData.totalHouseScore,
                    borderColor: 'rgba(255, 99, 132, 1)',
                    backgroundColor: 'rgba(255, 99, 132, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowHouse
                });
                datasets.push({
                    id: 'ability',
                    label: t('abilityValue'),
                    data: historyData.abilityScore,
                    borderColor: 'rgba(54, 162, 235, 1)',
                    backgroundColor: 'rgba(54, 162, 235, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowAbility
                });
            } else {
                datasets.push({
                    id: 'current',
                    label: t('currentAssetsValue'),
                    data: historyData.currentAssets,
                    borderColor: 'rgba(75, 192, 192, 1)',
                    backgroundColor: 'rgba(75, 192, 192, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.summaryShowCurrent
                });
                datasets.push({
                    id: 'nonCurrent',
                    label: t('nonCurrentAssetsValue'),
                    data: historyData.nonCurrentAssets,
                    borderColor: 'rgba(255, 99, 132, 1)',
                    backgroundColor: 'rgba(255, 99, 132, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.summaryShowNonCurrent
                });
            }

            datasets.push({
                id: 'total',
                label: t('totalAssets'),
                data: historyData.totalAssets,
                borderColor: 'rgba(54, 162, 235, 1)',
                backgroundColor: 'rgba(54, 162, 235, 0.1)',
                tension: 0.3,
                fill: false,
                hidden: !options.showTotal,
                borderWidth: 2
            });

            chart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: historyData.labels,
                    datasets: datasets
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            display: true,
                            position: 'top',
                            labels: {
                                usePointStyle: true,
                                boxWidth: 10,
                                color: '#fff'
                            }
                        },
                        tooltip: {
                            callbacks: {
                                label: (context) => {
                                    const label = context.dataset.label || '';
                                    const value = formatLargeNumber(context.raw, options.unitMode);
                                    return `${label}: ${value}`;
                                }
                            },
                            backgroundColor: 'rgba(0, 0, 0, 0.8)',
                            titleColor: '#fff',
                            bodyColor: '#fff'
                        }
                    },
                    scales: {
                        x: {
                            ticks: {
                                color: '#ccc'
                            },
                            grid: {
                                color: 'rgba(255, 255, 255, 0.1)'
                            }
                        },
                        y: {
                            ticks: {
                                color: '#ccc',
                                callback: (value) => formatLargeNumber(value, options.unitMode)
                            },
                            grid: {
                                color: 'rgba(255, 255, 255, 0.1)'
                            }
                        }
                    }
                }
            });
        }

        function updateChart() {
            const options = getChartOptions();
            const historyData = store.getHistoryData(options.daysToShow);

            chart.data.labels = historyData.labels;

            if (options.viewMode === 'detailed') {
                if (chart.data.datasets.length >= 6) {
                    chart.data.datasets[0].data = historyData.equippedNetworth;
                    chart.data.datasets[1].data = historyData.inventoryNetworth;
                    chart.data.datasets[2].data = historyData.marketListingsNetworth;
                    chart.data.datasets[3].data = historyData.totalHouseScore;
                    chart.data.datasets[4].data = historyData.abilityScore;
                }
            } else {
                if (chart.data.datasets.length >= 3) {
                    chart.data.datasets[0].data = historyData.currentAssets;
                    chart.data.datasets[1].data = historyData.nonCurrentAssets;
                }
            }

            const totalIndex = options.viewMode === 'detailed' ? 5 : 2;
            if (chart.data.datasets[totalIndex]) {
                chart.data.datasets[totalIndex].data = historyData.totalAssets;
            }

            chart.options.scales.y.ticks.callback = (value) => formatLargeNumber(value, options.unitMode);
            chart.options.plugins.tooltip.callbacks.label = (context) => {
                const label = context.dataset.label || '';
                const value = formatLargeNumber(context.raw, options.unitMode);
                return `${label}: ${value}`;
            };

            chart.update();
        }

        function updateChartVisibility() {
            const options = getChartOptions();

            options.summaryShowCurrent = document.getElementById('summaryShowCurrent').checked;
            options.summaryShowNonCurrent = document.getElementById('summaryShowNonCurrent').checked;
            options.detailedShowEquipped = document.getElementById('detailedShowEquipped').checked;
            options.detailedShowInventory = document.getElementById('detailedShowInventory').checked;
            options.detailedShowMarketListings = document.getElementById('detailedShowMarketListings').checked;
            options.detailedShowHouse = document.getElementById('detailedShowHouse').checked;
            options.detailedShowAbility = document.getElementById('detailedShowAbility').checked;
            options.showTotal = document.getElementById('showTotalOption').checked;

            saveChartOptions(options);

            if (chart) {
                if (options.viewMode === 'detailed') {
                    if (chart.data.datasets.length >= 6) {
                        chart.data.datasets[0].hidden = !options.detailedShowEquipped;
                        chart.data.datasets[1].hidden = !options.detailedShowInventory;
                        chart.data.datasets[2].hidden = !options.detailedShowMarketListings;
                        chart.data.datasets[3].hidden = !options.detailedShowHouse;
                        chart.data.datasets[4].hidden = !options.detailedShowAbility;
                        chart.data.datasets[5].hidden = !options.showTotal;
                    }
                } else {
                    if (chart.data.datasets.length >= 3) {
                        chart.data.datasets[0].hidden = !options.summaryShowCurrent;
                        chart.data.datasets[1].hidden = !options.summaryShowNonCurrent;
                        chart.data.datasets[2].hidden = !options.showTotal;
                    }
                }
                chart.update();
            }
        }

        function toggleTimeRangeVisibility() {
            const show = document.getElementById('showTimeRangeToggle').checked;
            const timeRangeOptions = document.getElementById('timeRangeOptions');

            const options = getChartOptions();
            options.showTimeRangeSettings = show;
            saveChartOptions(options);

            if (show) {
                timeRangeOptions.classList.remove('hidden');
            } else {
                timeRangeOptions.classList.add('hidden');
            }
        }

        function updateTimeRangeSettings() {
            const options = getChartOptions();

            const visibleRanges = [];
            if (document.getElementById('timeRange3').checked) visibleRanges.push(3);
            if (document.getElementById('timeRange7').checked) visibleRanges.push(7);
            if (document.getElementById('timeRange30').checked) visibleRanges.push(30);
            if (document.getElementById('timeRange60').checked) visibleRanges.push(60);
            if (document.getElementById('timeRange90').checked) visibleRanges.push(90);
            if (document.getElementById('timeRange180').checked) visibleRanges.push(180);

            options.visibleTimeRanges = visibleRanges;
            saveChartOptions(options);

            updateChartTimeRangeButtons();
            updateSettingsTimeRangeButtons();
        }

        function updateChartTimeRangeButtons() {
            const timeRangeOptions = document.getElementById('timeRangeOptions');
            const options = getChartOptions();

            const titleSpan = timeRangeOptions.querySelector('span');
            timeRangeOptions.innerHTML = '';
            if (titleSpan) {
                timeRangeOptions.appendChild(titleSpan);
            }

            options.visibleTimeRanges.forEach(days => {
                const btn = document.createElement('button');
                btn.id = `btn${days}Days`;
                btn.className = 'time-range-btn';
                if (options.daysToShow === days) {
                    btn.classList.add('active');
                }
                btn.textContent = `${days}${t('days')}`;
                btn.addEventListener('click', () => updateChartTimeRange(days));
                timeRangeOptions.appendChild(btn);
            });
        }

        function updateSettingsTimeRangeButtons() {
            const buttonsContainer = document.getElementById('settingsTimeRangeButtons');
            const options = getChartOptions();

            if (!buttonsContainer) return;

            buttonsContainer.innerHTML = '';

            options.visibleTimeRanges.forEach(days => {
                const btn = document.createElement('button');
                btn.id = `settingsBtn${days}Days`;
                btn.className = 'time-range-btn';
                if (options.daysToShow === days) {
                    btn.classList.add('active');
                }
                btn.textContent = `${days}${t('days')}`;
                btn.addEventListener('click', () => {
                    updateChartTimeRange(days);
                    updateSettingsTimeRangeButtons();
                    updateChartTimeRangeButtons();
                });
                buttonsContainer.appendChild(btn);
            });
        }

        function updateChartTimeRange(days) {
            const options = getChartOptions();
            options.daysToShow = days;
            saveChartOptions(options);

            updateChartTimeRangeButtons();
            updateSettingsTimeRangeButtons();

            if (chart) {
                const historyData = store.getHistoryData(days);
                chart.data.labels = historyData.labels;

                if (options.viewMode === 'detailed') {
                    if (chart.data.datasets.length >= 6) {
                        chart.data.datasets[0].data = historyData.equippedNetworth;
                        chart.data.datasets[1].data = historyData.inventoryNetworth;
                        chart.data.datasets[2].data = historyData.marketListingsNetworth;
                        chart.data.datasets[3].data = historyData.totalHouseScore;
                        chart.data.datasets[4].data = historyData.abilityScore;
                    }
                } else {
                    if (chart.data.datasets.length >= 3) {
                        chart.data.datasets[0].data = historyData.currentAssets;
                        chart.data.datasets[1].data = historyData.nonCurrentAssets;
                    }
                }

                const totalIndex = options.viewMode === 'detailed' ? 5 : 2;
                if (chart.data.datasets[totalIndex]) {
                    chart.data.datasets[totalIndex].data = historyData.totalAssets;
                }

                chart.update();
            }
        }

        updateDisplay(true);
        setInterval(() => updateDisplay(false), 10 * 60 * 1000);
    };

    /* =========================
       页面检测与执行
    ========================= */
    const checkAssetsAndRun = async () => {
        console.log('[DailyAssets] 开始检查资产数据...');
        const mwValues = await getMWIToolsValues();
        console.log('[DailyAssets] 最终读取结果:', mwValues);

        const insertDom = document.getElementById('netWorthDetails');
        if (insertDom && !document.getElementById('assetDeltaContainer')) {
            window.kbd_calculateTotalNetworth?.(
                mwValues.equippedNetworth,
                mwValues.inventoryNetworth,
                mwValues.marketListingsNetworth,
                mwValues.totalHouseScore,
                mwValues.abilityScore,
                insertDom
            );
        }
    };

    window.addEventListener('load', () => {
        setTimeout(checkAssetsAndRun, 5000);
    });

    if (document.readyState === 'complete') {
        setTimeout(checkAssetsAndRun, 5000);
    }

    setInterval(checkAssetsAndRun, 60000);
})();