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);
})();