import { CoinIcon, ActiveUsersIcon, CrownIcon, StarUserIcon, TrendIcon, UserPlusIcon } from "../components/MetricIcons.jsx";
import { countryFlag, resolveCountryMeta, stripCountryFlagPrefix } from "./countryMeta.js";
import { formatCoin, formatMoney, formatMoneyFull, formatNumber, formatPercent, formatWholeMoney } from "../utils/format.js";
import { numberValue, ratio } from "../utils/number.js";
import { formatDateTime, sameRange, todayRange } from "../utils/time.js";

export function createDashboardModel(overview, { appCode, countryId, range, timeZone }) {
  const source = overview || {};
  const comparisonCaption = isTodayRange(range, timeZone) ? "与昨日相比" : "近 7 日";
  const recharge = effectiveRechargeUSDMinor(source);
  const userRecharge = effectiveUserRechargeUSDMinor(source);
  const newUserRecharge = readNumber(source, "new_user_recharge_usd_minor", "newUserRechargeUsdMinor");
  const newPaidUsers = readNumber(source, "new_paid_users", "newPaidUsers");
  const activeUsers = readNumber(source, "active_users", "activeUsers");
  const paidUsers = readNumber(source, "paid_users", "paidUsers");
  const rechargeUsers = readNumber(source, "recharge_users", "rechargeUsers", "paid_users", "paidUsers");
  const newUsers = readNumber(source, "new_users", "newUsers", "registrations", "registered_users", "registeredUsers");
  const arpu = readNumber(source, "arpu_usd_minor", "arpuUsdMinor");
  const arppu = readNumber(source, "arppu_usd_minor", "arppuUsdMinor");
  const giftCoinSpent = readNumber(source, "gift_coin_spent", "giftCoinSpent");
  const coinSellerTransferCoin = readNumber(source, "coin_seller_transfer_coin", "coinSellerTransferCoin");
  const realRoomRobotNormalGiftCoin = readNumber(source, "real_room_robot_normal_gift_coin", "realRoomRobotNormalGiftCoin");
  const realRoomRobotLuckyGiftCoin = readNumber(source, "real_room_robot_lucky_gift_coin", "realRoomRobotLuckyGiftCoin");
  const realRoomRobotSuperGiftCoin = readNumber(source, "real_room_robot_super_gift_coin", "realRoomRobotSuperGiftCoin");
  const realRoomRobotTotalGiftCoin =
    readNumber(source, "real_room_robot_total_gift_coin", "realRoomRobotTotalGiftCoin") || realRoomRobotNormalGiftCoin + realRoomRobotLuckyGiftCoin + realRoomRobotSuperGiftCoin;
  const realRoomRobotGiftRoomCount = readNumber(source, "real_room_robot_gift_room_count", "realRoomRobotGiftRoomCount");
  const realRoomRobotAvgRoomGiftCoin =
    readNumber(source, "real_room_robot_avg_room_gift_coin", "realRoomRobotAvgRoomGiftCoin") || Math.round(ratio(realRoomRobotTotalGiftCoin, realRoomRobotGiftRoomCount));
  const luckyGiftPools = normalizeLuckyGiftPools(source);
  const luckyGiftPoolTotals = summarizeLuckyGiftPools(luckyGiftPools);
  const luckyGiftTurnover = luckyGiftPools.length ? luckyGiftPoolTotals.turnover : readNumber(source, "room_lucky_gift_turnover", "roomLuckyGiftTurnover", "lucky_gift_turnover", "luckyGiftTurnover");
  const luckyGiftPayout = luckyGiftPools.length ? luckyGiftPoolTotals.payout : readNumber(source, "lucky_gift_payout", "luckyGiftPayout");
  const luckyGiftProfit = luckyGiftPools.length ? luckyGiftPoolTotals.profit : readNumber(source, "lucky_gift_profit", "luckyGiftProfit");
  const gameTurnover = readNumber(source, "game_turnover", "gameTurnover");
  const gameProfit = readNumber(source, "game_profit", "gameProfit");
  const countryBreakdown = normalizeCountries(source, countryId);
  const dailyCountryBreakdown = normalizeDailyCountryBreakdown(source);
  const dailySeries = normalizeDailySeries(source);
  const revenueSeries = dailySeries.length ? dailySeries : normalizeRevenueSeries(source);
  const payoutDistribution = normalizeDistribution(source);
  const gameRanking = normalizeGameRanking(source);
  const reportCountryRows = buildReportCountryRows({
    countryBreakdown,
    dailyCountryBreakdown,
    dailySeries,
    range,
    source,
  });

  return {
    appCode,
    businessKpis: [
      coinMetric("礼物消费", giftCoinSpent, readDelta(source, "gift_coin_spent_delta_rate", "giftCoinSpentDeltaRate"), comparisonCaption, {
        trend: metricTrend(dailySeries, "gift_coin_spent", "礼物消费", "coin"),
      }),
      coinMetric("币商出货金币", coinSellerTransferCoin, readDelta(source, "coin_seller_transfer_coin_delta_rate", "coinSellerTransferCoinDeltaRate"), comparisonCaption, {
        trend: metricTrend(dailySeries, "coin_seller_transfer_coin", "币商出货金币", "coin"),
      }),
      coinMetric(
        "幸运礼物流水",
        luckyGiftTurnover,
        readDelta(source, "room_lucky_gift_turnover_delta_rate", "lucky_gift_turnover_delta_rate", "roomLuckyGiftTurnoverDeltaRate", "luckyGiftTurnoverDeltaRate"),
        comparisonCaption,
        {
          detailKey: "luckyGiftPools",
          trend: metricTrend(dailySeries, "lucky_gift_turnover", "幸运礼物流水", "coin"),
        },
      ),
      coinMetric("幸运礼物返奖", luckyGiftPayout, readDelta(source, "lucky_gift_payout_delta_rate", "luckyGiftPayoutDeltaRate"), comparisonCaption, {
        detailKey: "luckyGiftPools",
        trend: metricTrend(dailySeries, "lucky_gift_payout", "幸运礼物返奖", "coin"),
      }),
      coinMetric("幸运礼物利润", luckyGiftProfit, readDelta(source, "lucky_gift_profit_delta_rate", "luckyGiftProfitDeltaRate"), comparisonCaption, {
        detailKey: "luckyGiftPools",
        trend: metricTrend(dailySeries, "lucky_gift_profit", "幸运礼物利润", "coin"),
      }),
      coinMetric("游戏流水", gameTurnover, readDelta(source, "game_turnover_delta_rate", "gameTurnoverDeltaRate"), comparisonCaption, {
        trend: metricTrend(dailySeries, "game_turnover", "游戏流水", "coin"),
      }),
      coinMetric("游戏利润", gameProfit, readDelta(source, "game_profit_delta_rate", "gameProfitDeltaRate"), comparisonCaption, { trend: metricTrend(dailySeries, "game_profit", "游戏利润", "coin") }),
    ],
    countryBreakdown,
    dailyCountryBreakdown,
    funnel: [
      { name: "活跃用户", rate: 1, value: activeUsers },
      { name: "付费用户", rate: ratio(paidUsers, activeUsers), value: paidUsers },
      { name: "充值用户", rate: ratio(rechargeUsers, activeUsers), value: rechargeUsers },
    ],
    gameMetrics: [
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "game_turnover_delta_rate", "gameTurnoverDeltaRate")),
        label: "流水",
        value: formatWholeMoney(source.game_turnover),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "game_payout_delta_rate", "gamePayoutDeltaRate")),
        label: "派奖",
        value: formatWholeMoney(source.game_payout),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "game_refund_delta_rate", "gameRefundDeltaRate")),
        label: "退款",
        value: formatWholeMoney(source.game_refund),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "game_profit_delta_rate", "gameProfitDeltaRate")),
        label: "利润",
        value: formatWholeMoney(source.game_profit),
      },
    ],
    gameRanking,
    giftRanking: normalizeGiftRanking(source),
    kpis: [
      {
        caption: comparisonCaption,
        delta: readDelta(source, "recharge_delta_rate", "rechargeDeltaRate", "total_recharge_delta_rate", "totalRechargeDeltaRate"),
        icon: CoinIcon,
        label: "总充值",
        trend: metricTrend(dailySeries, "recharge_usd_minor", "总充值", "money"),
        trendFormat: "money",
        value: formatMoneyFull(recharge),
      },
      {
        icon: UserPlusIcon,
        label: "用户充值 / 新用户充值",
        subMetrics: [
          {
            caption: comparisonCaption,
            delta: readDelta(source, "user_recharge_delta_rate", "userRechargeDeltaRate", "recharge_delta_rate", "rechargeDeltaRate"),
            label: "用户充值",
            value: formatMoneyFull(userRecharge),
          },
          {
            caption: comparisonCaption,
            delta: readDelta(source, "new_user_recharge_delta_rate", "newUserRechargeDeltaRate"),
            label: "新用户充值",
            value: formatMoneyFull(newUserRecharge),
          },
        ],
        trend: [...metricTrend(dailySeries, "user_recharge_usd_minor", "用户充值", "money"), ...metricTrend(dailySeries, "new_user_recharge_usd_minor", "新用户充值", "money")],
        trendFormat: "money",
      },
      {
        caption: comparisonCaption,
        delta: readDelta(source, "new_users_delta_rate", "newUsersDeltaRate"),
        icon: UserPlusIcon,
        label: "新增用户数",
        trend: metricTrend(dailySeries, "new_users", "新增用户数", "number"),
        trendFormat: "number",
        value: formatNumber(newUsers),
      },
      {
        caption: comparisonCaption,
        delta: readDelta(source, "active_users_delta_rate", "activeUsersDeltaRate"),
        icon: ActiveUsersIcon,
        label: "活跃用户",
        trend: metricTrend(dailySeries, "active_users", "活跃用户", "number"),
        trendFormat: "number",
        value: formatNumber(activeUsers),
      },
      {
        icon: CrownIcon,
        label: "付费用户 / 新增付费用户",
        subMetrics: [
          {
            caption: comparisonCaption,
            delta: readDelta(source, "paid_users_delta_rate", "paidUsersDeltaRate"),
            label: "付费用户",
            value: formatNumber(paidUsers),
          },
          {
            caption: comparisonCaption,
            delta: readDelta(source, "new_paid_users_delta_rate", "newPaidUsersDeltaRate"),
            label: "新增付费用户",
            value: formatNumber(newPaidUsers),
          },
        ],
        trend: [...metricTrend(dailySeries, "paid_users", "付费用户", "number"), ...metricTrend(dailySeries, "new_paid_users", "新增付费用户", "number")],
        trendFormat: "number",
      },
      {
        caption: comparisonCaption,
        delta: readDelta(source, "arpu_delta_rate", "arpuDeltaRate"),
        deltaTone: deltaTone(source, "arpu_delta_rate", "arpuDeltaRate"),
        icon: TrendIcon,
        label: "ARPU",
        trend: metricTrend(dailySeries, "arpu_usd_minor", "ARPU", "money"),
        trendFormat: "money",
        value: formatMoney(arpu),
      },
      {
        caption: comparisonCaption,
        delta: readDelta(source, "arppu_delta_rate", "arppuDeltaRate"),
        icon: StarUserIcon,
        label: "ARPPU",
        trend: metricTrend(dailySeries, "arppu_usd_minor", "ARPPU", "money"),
        trendFormat: "money",
        value: formatMoney(arppu),
      },
    ],
    luckyMetrics: [
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "lucky_gift_turnover_delta_rate", "luckyGiftTurnoverDeltaRate")),
        label: "流水",
        value: formatWholeMoney(luckyGiftTurnover),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "lucky_gift_payout_delta_rate", "luckyGiftPayoutDeltaRate")),
        label: "返奖",
        value: formatWholeMoney(luckyGiftPayout),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "lucky_gift_profit_delta_rate", "luckyGiftProfitDeltaRate")),
        label: "利润",
        value: formatWholeMoney(luckyGiftProfit),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "lucky_gift_payout_rate_delta_pp", "luckyGiftPayoutRateDeltaPp"), "pp"),
        label: "返奖率",
        value: formatPercent(source.lucky_gift_payout_rate),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "lucky_gift_profit_rate_delta_pp", "luckyGiftProfitRateDeltaPp"), "pp"),
        label: "利润率",
        value: formatPercent(source.lucky_gift_profit_rate),
      },
    ],
    luckyGiftPools,
    payoutDistribution,
    reportCountryRows,
    reportMetricSources: Array.isArray(source.report_metric_sources) ? source.report_metric_sources : [],
    revenueSeries,
    robotGiftKpis: [
      coinMetric("真人房机器人普通礼物", realRoomRobotNormalGiftCoin, "", "当前筛选", {
        trend: metricTrend(dailySeries, "real_room_robot_normal_gift_coin", "真人房机器人普通礼物", "coin"),
      }),
      coinMetric("真人房机器人幸运礼物", realRoomRobotLuckyGiftCoin, "", "当前筛选", {
        trend: metricTrend(dailySeries, "real_room_robot_lucky_gift_coin", "真人房机器人幸运礼物", "coin"),
      }),
      coinMetric("真人房机器人超级幸运礼物", realRoomRobotSuperGiftCoin, "", "当前筛选", {
        trend: metricTrend(dailySeries, "real_room_robot_super_gift_coin", "真人房机器人超级幸运礼物", "coin"),
      }),
      coinMetric("真人房机器人房均礼物", realRoomRobotAvgRoomGiftCoin, "", `${formatNumber(realRoomRobotGiftRoomCount)} 个房间`, {
        trend: metricTrend(dailySeries, "real_room_robot_avg_room_gift_coin", "真人房机器人房均礼物", "coin"),
      }),
    ],
    roomMetrics: [
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "gift_coin_spent_delta_rate", "giftCoinSpentDeltaRate")),
        label: "礼物消费(充值)",
        value: formatWholeMoney(source.gift_coin_spent),
      },
      {
        caption: captionWithDelta(
          comparisonCaption,
          readDelta(source, "room_lucky_gift_turnover_delta_rate", "lucky_gift_turnover_delta_rate", "roomLuckyGiftTurnoverDeltaRate", "luckyGiftTurnoverDeltaRate"),
        ),
        label: "幸运礼物流水",
        value: formatWholeMoney(source.room_lucky_gift_turnover ?? source.lucky_gift_turnover),
      },
    ],
    sideMetrics: [
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "arpu_delta_rate", "arpuDeltaRate")),
        deltaTone: deltaTone(source, "arpu_delta_rate", "arpuDeltaRate"),
        label: "ARPU",
        value: formatMoney(arpu),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "payer_rate_delta_pp", "payerRateDeltaPp"), "pp"),
        label: "付费转化率",
        value: formatPercent(ratio(paidUsers, activeUsers)),
      },
      {
        caption: captionWithDelta(comparisonCaption, readDelta(source, "arppu_delta_rate", "arppuDeltaRate")),
        label: "ARPPU",
        value: formatMoney(arppu),
      },
    ],
    topCountries: countryBreakdown.slice(0, 4),
    updatedAt: formatDateTime(source.updated_at_ms || source.updatedAtMs || Date.now(), timeZone),
  };
}

function coinMetric(label, value, delta, caption, extra = {}) {
  return { caption, delta, label, unit: "金币", unitIcon: CoinIcon, value: formatCoin(value), ...extra };
}

function captionWithDelta(label, delta) {
  return `${label} ${delta}`;
}

function readNumber(source, ...keys) {
  for (const key of keys) {
    if (source?.[key] !== undefined && source[key] !== null) {
      return numberValue(source[key]);
    }
  }
  return 0;
}

function readOptionalNumber(source, ...keys) {
  for (const key of keys) {
    if (source?.[key] !== undefined && source[key] !== null) {
      return numberValue(source[key]);
    }
  }
  return null;
}

function hasAnyKey(source, ...keys) {
  return keys.some((key) => source?.[key] !== undefined);
}

function readDelta(source, ...keys) {
  const unit = keys[keys.length - 1] === "pp" ? keys.pop() : "%";
  for (const key of keys) {
    if (source?.[key] !== undefined && source[key] !== null && source[key] !== "") {
      return formatDelta(source[key], unit);
    }
  }
  return "";
}

function deltaTone(source, ...keys) {
  for (const key of keys) {
    if (source?.[key] !== undefined && source[key] !== null && source[key] !== "") {
      return numberValue(source[key]) < 0 ? "down" : "up";
    }
  }
  return "up";
}

function formatDelta(value, unit) {
  const numeric = numberValue(value);
  const normalized = unit === "%" && Math.abs(numeric) <= 1 ? numeric * 100 : numeric;
  const prefix = normalized > 0 ? "+" : "";
  return `${prefix}${normalized.toFixed(2)}${unit}`;
}

function isTodayRange(range, timeZone) {
  return sameRange(range, todayRange(timeZone));
}

function normalizeRevenueSeries(source) {
  if (!source || !Object.keys(source).length) {
    return [];
  }
  if (Array.isArray(source.daily_series) && source.daily_series.length) {
    return source.daily_series.map((item) => {
      const coinSeller = numberValue(item.coin_seller ?? item.coin_seller_recharge_usd_minor ?? item.coinSellerRechargeUsdMinor);
      const google = numberValue(item.google ?? item.google_recharge_usd_minor ?? item.googleRechargeUsdMinor);
      const mifapay = numberValue(item.mifapay ?? item.mifa_pay ?? item.mifapay_recharge_usd_minor ?? item.mifaPayRechargeUsdMinor);
      const channelTotal = google + mifapay + coinSeller;
      const total = Math.max(numberValue(item.total ?? item.recharge_usd_minor), channelTotal);
      return {
        coin_seller: coinSeller,
        google,
        label: localizeSeriesLabel(item.label || item.stat_day || item.day || "-"),
        lineTotal: Math.max(numberValue(item.line_total ?? item.lineTotal ?? item.trend_total ?? item.trendTotal ?? total), channelTotal),
        mifapay,
        total,
      };
    });
  }
  const coinSeller = readNumber(source, "coin_seller_recharge_usd_minor", "coinSellerRechargeUsdMinor");
  const google = numberValue(source.google_recharge_usd_minor ?? source.googleRechargeUsdMinor);
  const mifapay = numberValue(source.mifapay_recharge_usd_minor ?? source.mifaPayRechargeUsdMinor);
  const total = effectiveRechargeUSDMinor(source);
  return [
    {
      coin_seller: coinSeller,
      google,
      label: "当前",
      lineTotal: total,
      mifapay,
      total,
    },
  ];
}

function normalizeCountries(source, countryId) {
  const raw = Array.isArray(source.country_breakdown) && source.country_breakdown.length ? source.country_breakdown : null;
  const rows =
    raw ||
    (countryId
      ? [
          {
            active_users: source.active_users,
            arppu_usd_minor: source.arppu_usd_minor,
            country: `国家 ${countryId}`,
            paid_users: source.paid_users,
            recharge_usd_minor: source.recharge_usd_minor,
            x: 56,
            y: 52,
          },
        ]
      : []);
  const totalRecharge = rows.reduce((sum, item) => sum + effectiveRechargeUSDMinor(item), 0) || 1;
  return rows
    .map((item, index) => ({
      ...normalizeCountryRow(item, index, totalRecharge),
    }))
    .sort((left, right) => right.recharge_usd_minor - left.recharge_usd_minor);
}

function normalizeDailyCountryBreakdown(source) {
  const rows = firstArray(source?.daily_country_breakdown, source?.dailyCountryBreakdown);
  if (!rows.length) {
    return [];
  }
  const totalRecharge = rows.reduce((sum, item) => sum + effectiveRechargeUSDMinor(item), 0) || 1;
  return rows.map((item, index) => ({
    ...normalizeCountryRow(item, index, totalRecharge),
    date_label: item.label || item.stat_day || item.statDay || item.day || "",
    stat_day: item.stat_day || item.statDay || item.day || "",
  }));
}

function buildReportCountryRows({ countryBreakdown, dailyCountryBreakdown, dailySeries, range, source }) {
  const totalRow = createReportRow(normalizeCountryRow({ ...source, country: "总计" }, 0, effectiveRechargeUSDMinor(source) || 1), {
    country: "总计",
    date_label: "全部",
    row_type: "total",
  });
  if (!isMultiDayRange(range)) {
    return [totalRow, ...countryBreakdown.map((row) => createReportRow(row, { date_label: range?.start || row.stat_day || "", row_type: "country" }))];
  }

  const dailyRowsByDay = groupRowsByDay(dailyCountryBreakdown);
  const dailySeriesByDay = new Map(dailySeries.map((item) => [item.stat_day || item.label, item]));
  const dayKeys = Array.from(new Set([...dailySeries.map((item) => item.stat_day || item.label).filter(Boolean), ...dailyCountryBreakdown.map((item) => item.stat_day).filter(Boolean)])).sort();

  if (!dayKeys.length) {
    return [totalRow, ...countryBreakdown.map((row) => createReportRow(row, { date_label: "汇总", row_type: "country" }))];
  }

  const rows = [totalRow];
  for (const day of dayKeys) {
    const dayCountries = dailyRowsByDay.get(day) || [];
    const dayTotalSource = { ...summarizeReportRows(dayCountries), ...(dailySeriesByDay.get(day) || {}) };
    const childRows = dayCountries.map((row) =>
      createReportRow(row, {
        date_label: day,
        row_type: "country",
        stat_day: day,
      }),
    );
    rows.push(
      createReportRow(normalizeCountryRow({ ...dayTotalSource, country: "总计" }, rows.length, effectiveRechargeUSDMinor(dayTotalSource) || 1), {
        children: childRows,
        country: "总计",
        date_label: day,
        row_type: "date_total",
        stat_day: day,
      }),
    );
  }
  return rows;
}

function createReportRow(row, extra) {
  return {
    ...row,
    ...extra,
    flag: extra.row_type === "country" ? row.flag : "",
    row_id: [extra.row_type, extra.stat_day || extra.date_label || "", row.country_id || row.country || "", row.region_id || ""].join(":"),
  };
}

function groupRowsByDay(rows) {
  return rows.reduce((groups, row) => {
    const day = row.stat_day || row.date_label || "";
    if (!day) {
      return groups;
    }
    if (!groups.has(day)) {
      groups.set(day, []);
    }
    groups.get(day).push(row);
    return groups;
  }, new Map());
}

function summarizeReportRows(rows) {
  const fields = [
    "active_users",
    "coin_seller_recharge_usd_minor",
    "coin_seller_stock_coin",
    "coin_seller_transfer_coin",
    "coin_total",
    "consumed_coin",
    "output_coin",
    "game_payout",
    "game_refund",
    "game_turnover",
    "gift_coin_spent",
    "google_recharge_usd_minor",
    "lucky_gift_payout",
    "lucky_gift_turnover",
    "mifapay_recharge_usd_minor",
    "mic_online_ms",
    "mic_online_users",
    "new_user_recharge_usd_minor",
    "paid_users",
    "platform_grant_coin",
    "recharge_usd_minor",
    "recharge_users",
    "registrations",
    "manual_grant_coin",
    "salary_usd_minor",
    "salary_transfer_coin",
    "super_lucky_gift_payout",
    "super_lucky_gift_turnover",
  ];
  const summary = fields.reduce((summary, key) => {
    summary[key] = sumOptionalField(rows, key);
    return summary;
  }, {});
  // 日期父行是多个国家行的合计，平均麦上时间必须按合计后的总时长/人数重算；如果下游 App 没有麦上时长字段，保留 null 让报表展示缺失而不是 0。
  summary.avg_mic_online_ms = summary.mic_online_ms === null || summary.mic_online_users === null ? null : Math.round(ratio(summary.mic_online_ms, summary.mic_online_users));
  return summary;
}

function sumOptionalField(rows, key) {
  let hasValue = false;
  const total = rows.reduce((sum, row) => {
    const value = row?.[key];
    if (value === undefined || value === null || Number.isNaN(Number(value))) {
      return sum;
    }
    hasValue = true;
    return sum + numberValue(value);
  }, 0);
  return hasValue ? total : null;
}

function isMultiDayRange(range) {
  return Boolean(range?.start && range?.end && range.start !== range.end);
}

function normalizeCountryRow(item, index, totalRecharge) {
  const code = item.country_code || item.countryCode || item.iso_code || item.isoCode;
  const countryId = numberValue(item.country_id ?? item.countryId);
  const country = stripCountryFlagPrefix(item.country || item.country_name || code || (countryId > 0 ? `国家 ${countryId || index + 1}` : "未知国家"));
  const meta = resolveCountryMeta({ code, name: country });
  const recharge = effectiveRechargeUSDMinor(item);
  const activeUsers = readOptionalNumber(item, "active_users", "activeUsers");
  const paidUsers = readOptionalNumber(item, "paid_users", "paidUsers");
  const rechargeUsers = readOptionalNumber(item, "recharge_users", "rechargeUsers", "paid_users", "paidUsers");
  const gameTurnover = readOptionalNumber(item, "game_turnover", "gameTurnover");
  const gamePayout = readOptionalNumber(item, "game_payout", "gamePayout");
  const gameRefund = readOptionalNumber(item, "game_refund", "gameRefund") ?? 0;
  const gameProfit = readOptionalNumber(item, "game_profit", "gameProfit") ?? (gameTurnover === null || gamePayout === null ? null : gameTurnover - gamePayout - gameRefund);
  const luckyGiftTurnover = readOptionalNumber(item, "lucky_gift_turnover", "luckyGiftTurnover", "room_lucky_gift_turnover", "roomLuckyGiftTurnover");
  const luckyGiftPayout = readOptionalNumber(item, "lucky_gift_payout", "luckyGiftPayout");
  const luckyGiftProfit = readOptionalNumber(item, "lucky_gift_profit", "luckyGiftProfit") ?? (luckyGiftTurnover === null || luckyGiftPayout === null ? null : luckyGiftTurnover - luckyGiftPayout);
  const superLuckyGiftTurnover = readOptionalNumber(item, "super_lucky_gift_turnover", "superLuckyGiftTurnover");
  const superLuckyGiftPayout = readOptionalNumber(item, "super_lucky_gift_payout", "superLuckyGiftPayout");
  const superLuckyGiftProfit =
    readOptionalNumber(item, "super_lucky_gift_profit", "superLuckyGiftProfit") ??
    (superLuckyGiftTurnover === null || superLuckyGiftPayout === null ? null : superLuckyGiftTurnover - superLuckyGiftPayout);
  const micOnlineMS = readOptionalNumber(item, "mic_online_ms", "micOnlineMs");
  const micOnlineUsers = readOptionalNumber(item, "mic_online_users", "micOnlineUsers");
  const avgMicOnlineMS = readOptionalNumber(item, "avg_mic_online_ms", "avgMicOnlineMs") ?? (micOnlineMS === null || micOnlineUsers === null ? null : Math.round(ratio(micOnlineMS, micOnlineUsers)));
  const consumedCoin = readOptionalNumber(item, "consumed_coin", "consumedCoin");
  const outputCoin = readOptionalNumber(item, "output_coin", "outputCoin");
  const consumeOutputRatio = readOptionalNumber(item, "consume_output_ratio", "consumeOutputRatio") ?? (consumedCoin === null || outputCoin === null ? null : ratio(outputCoin, consumedCoin));
  const consumeOutputDelta = readOptionalNumber(item, "consume_output_delta", "consumeOutputDelta") ?? (consumedCoin === null || outputCoin === null ? null : consumedCoin - outputCoin);
  return {
    active_users: activeUsers,
    arpu_usd_minor: readOptionalNumber(item, "arpu_usd_minor", "arpuUsdMinor"),
    arppu_usd_minor: readOptionalNumber(item, "arppu_usd_minor", "arppuUsdMinor"),
    avg_recharge_usd_minor: readOptionalNumber(item, "avg_recharge_usd_minor", "avgRechargeUsdMinor"),
    avg_mic_online_ms: avgMicOnlineMS,
    coin_seller_recharge_usd_minor: readOptionalNumber(item, "coin_seller_recharge_usd_minor", "coinSellerRechargeUsdMinor"),
    coin_seller_stock_coin: readOptionalNumber(item, "coin_seller_stock_coin", "coinSellerStockCoin"),
    coin_seller_transfer_coin: readOptionalNumber(item, "coin_seller_transfer_coin", "coinSellerTransferCoin"),
    coin_total: readOptionalNumber(item, "coin_total", "coinTotal"),
    consumed_coin: consumedCoin,
    consume_output_delta: consumeOutputDelta,
    consume_output_ratio: consumeOutputRatio,
    country: meta?.label || country,
    country_id: countryId,
    country_code: code || meta?.code || "",
    flag: countryFlag(code || meta?.code),
    game_payout: gamePayout,
    game_profit: gameProfit,
    game_profit_rate: readOptionalNumber(item, "game_profit_rate", "gameProfitRate") ?? (gameProfit === null || gameTurnover === null ? null : ratio(gameProfit, gameTurnover)),
    game_refund: gameRefund,
    game_turnover: gameTurnover,
    geo_point: Array.isArray(item.geo_point) ? item.geo_point : meta?.point,
    gift_coin_spent: readOptionalNumber(item, "gift_coin_spent", "giftCoinSpent"),
    google_recharge_usd_minor: readOptionalNumber(item, "google_recharge_usd_minor", "googleRechargeUsdMinor"),
    lucky_gift_payout: luckyGiftPayout,
    lucky_gift_profit: luckyGiftProfit,
    lucky_gift_profit_rate:
      readOptionalNumber(item, "lucky_gift_profit_rate", "luckyGiftProfitRate") ?? (luckyGiftProfit === null || luckyGiftTurnover === null ? null : ratio(luckyGiftProfit, luckyGiftTurnover)),
    lucky_gift_turnover: luckyGiftTurnover,
    manual_grant_coin: readOptionalNumber(item, "manual_grant_coin", "manualGrantCoin"),
    mic_online_ms: micOnlineMS,
    mic_online_users: micOnlineUsers,
    mifapay_recharge_usd_minor: readOptionalNumber(item, "mifapay_recharge_usd_minor", "mifapayRechargeUsdMinor", "mifa_pay_recharge_usd_minor", "mifaPayRechargeUsdMinor"),
    new_user_recharge_usd_minor: readOptionalNumber(item, "new_user_recharge_usd_minor", "newUserRechargeUsdMinor"),
    paid_users: paidUsers,
    payer_rate:
      readOptionalNumber(item, "payer_rate", "payerRate", "paid_conversion_rate", "paidConversionRate") ?? (paidUsers === null || activeUsers === null ? null : ratio(paidUsers, activeUsers)),
    output_coin: outputCoin,
    platform_grant_coin: readOptionalNumber(item, "platform_grant_coin", "platformGrantCoin"),
    recharge_usd_minor: recharge,
    recharge_conversion_rate: hasAnyKey(item, "recharge_conversion_rate", "rechargeConversionRate")
      ? readOptionalNumber(item, "recharge_conversion_rate", "rechargeConversionRate")
      : rechargeUsers === null || activeUsers === null
        ? null
        : ratio(rechargeUsers, activeUsers),
    recharge_users: rechargeUsers,
    region_id: numberValue(item.region_id ?? item.regionId),
    salary_usd_minor: readOptionalNumber(item, "salary_usd_minor", "salaryUsdMinor"),
    salary_transfer_coin: readOptionalNumber(item, "salary_transfer_coin", "salaryTransferCoin"),
    share: recharge / totalRecharge,
    registrations: readOptionalNumber(item, "new_users", "newUsers", "registrations", "registered_users", "registeredUsers"),
    super_lucky_gift_payout: superLuckyGiftPayout,
    super_lucky_gift_profit: superLuckyGiftProfit,
    super_lucky_gift_profit_rate:
      readOptionalNumber(item, "super_lucky_gift_profit_rate", "superLuckyGiftProfitRate") ??
      (superLuckyGiftProfit === null || superLuckyGiftTurnover === null ? null : ratio(superLuckyGiftProfit, superLuckyGiftTurnover)),
    super_lucky_gift_turnover: superLuckyGiftTurnover,
    trend_rate: numberValue(item.trend_rate ?? item.trendRate),
  };
}

function effectiveRechargeUSDMinor(source) {
  const direct = readNumber(source, "recharge_usd_minor", "rechargeUsdMinor", "total_recharge_usd_minor", "totalRechargeUsdMinor");
  const channelTotal =
    readNumber(source, "mifapay_recharge_usd_minor", "mifapayRechargeUsdMinor", "mifa_pay_recharge_usd_minor", "mifaPayRechargeUsdMinor") +
    readNumber(source, "google_recharge_usd_minor", "googleRechargeUsdMinor") +
    readNumber(source, "coin_seller_recharge_usd_minor", "coinSellerRechargeUsdMinor");
  return Math.max(direct, channelTotal);
}

function effectiveUserRechargeUSDMinor(source) {
  const google = readNumber(source, "google_recharge_usd_minor", "googleRechargeUsdMinor");
  const mifapay = readNumber(source, "mifapay_recharge_usd_minor", "mifapayRechargeUsdMinor", "mifa_pay_recharge_usd_minor", "mifaPayRechargeUsdMinor");
  const channelUserRecharge = google + mifapay;
  const direct = readNumber(source, "user_recharge_usd_minor", "userRechargeUsdMinor", "recharge_usd_minor", "rechargeUsdMinor");
  const coinSeller = readNumber(source, "coin_seller_recharge_usd_minor", "coinSellerRechargeUsdMinor");
  return Math.max(channelUserRecharge, Math.max(0, direct - coinSeller));
}

function normalizeDailySeries(source) {
  if (!Array.isArray(source?.daily_series) || !source.daily_series.length) {
    return [];
  }
  return source.daily_series.map((item) => {
    const googleMetric = readOptionalNumber(item, "google_recharge_usd_minor", "googleRechargeUsdMinor", "google");
    const mifapayMetric = readOptionalNumber(item, "mifapay_recharge_usd_minor", "mifapayRechargeUsdMinor", "mifa_pay_recharge_usd_minor", "mifaPayRechargeUsdMinor", "mifapay");
    const coinSellerMetric = readOptionalNumber(item, "coin_seller_recharge_usd_minor", "coinSellerRechargeUsdMinor", "coin_seller");
    const google = googleMetric ?? 0;
    const mifapay = mifapayMetric ?? 0;
    const coinSeller = coinSellerMetric ?? 0;
    const channelTotal = google + mifapay + coinSeller;
    const recharge = Math.max(effectiveRechargeUSDMinor(item), channelTotal);
    const userRecharge = Math.max(google + mifapay, Math.max(0, readNumber(item, "user_recharge_usd_minor", "userRechargeUsdMinor", "recharge_usd_minor", "rechargeUsdMinor") - coinSeller));
    const gameTurnover = readOptionalNumber(item, "game_turnover", "gameTurnover");
    const gamePayout = readOptionalNumber(item, "game_payout", "gamePayout");
    const gameProfit = readOptionalNumber(item, "game_profit", "gameProfit");
    const luckyGiftTurnover = readOptionalNumber(item, "lucky_gift_turnover", "luckyGiftTurnover", "room_lucky_gift_turnover", "roomLuckyGiftTurnover");
    const luckyGiftPayout = readOptionalNumber(item, "lucky_gift_payout", "luckyGiftPayout");
    const luckyGiftProfit = readOptionalNumber(item, "lucky_gift_profit", "luckyGiftProfit") ?? (luckyGiftTurnover === null || luckyGiftPayout === null ? null : luckyGiftTurnover - luckyGiftPayout);
    const micOnlineMS = readOptionalNumber(item, "mic_online_ms", "micOnlineMs");
    const micOnlineUsers = readOptionalNumber(item, "mic_online_users", "micOnlineUsers");
    const avgMicOnlineMS = readOptionalNumber(item, "avg_mic_online_ms", "avgMicOnlineMs") ?? (micOnlineMS === null || micOnlineUsers === null ? null : Math.round(ratio(micOnlineMS, micOnlineUsers)));
    const consumedCoin = readOptionalNumber(item, "consumed_coin", "consumedCoin");
    const outputCoin = readOptionalNumber(item, "output_coin", "outputCoin");
    const consumeOutputRatio = readOptionalNumber(item, "consume_output_ratio", "consumeOutputRatio") ?? (consumedCoin === null || outputCoin === null ? null : ratio(outputCoin, consumedCoin));
    const consumeOutputDelta = readOptionalNumber(item, "consume_output_delta", "consumeOutputDelta") ?? (consumedCoin === null || outputCoin === null ? null : consumedCoin - outputCoin);
    return {
      active_users: readOptionalNumber(item, "active_users", "activeUsers"),
      arppu_usd_minor: readOptionalNumber(item, "arppu_usd_minor", "arppuUsdMinor"),
      arpu_usd_minor: readOptionalNumber(item, "arpu_usd_minor", "arpuUsdMinor"),
      avg_mic_online_ms: avgMicOnlineMS,
      coin_seller: coinSeller,
      coin_seller_recharge_usd_minor: coinSellerMetric,
      coin_seller_stock_coin: readOptionalNumber(item, "coin_seller_stock_coin", "coinSellerStockCoin"),
      coin_seller_transfer_coin: readOptionalNumber(item, "coin_seller_transfer_coin", "coinSellerTransferCoin"),
      coin_total: readOptionalNumber(item, "coin_total", "coinTotal"),
      consumed_coin: consumedCoin,
      consume_output_delta: consumeOutputDelta,
      consume_output_ratio: consumeOutputRatio,
      game_payout: gamePayout,
      game_profit: gameProfit,
      game_turnover: gameTurnover,
      gift_coin_spent: readOptionalNumber(item, "gift_coin_spent", "giftCoinSpent"),
      google,
      google_recharge_usd_minor: googleMetric,
      label: localizeSeriesLabel(item.label || item.stat_day || item.day || "-"),
      lineTotal: Math.max(readNumber(item, "line_total", "lineTotal", "trend_total", "trendTotal", "total"), recharge, channelTotal),
      lucky_gift_payout: luckyGiftPayout,
      lucky_gift_profit: luckyGiftProfit,
      lucky_gift_turnover: luckyGiftTurnover,
      manual_grant_coin: readOptionalNumber(item, "manual_grant_coin", "manualGrantCoin"),
      mic_online_ms: micOnlineMS,
      mic_online_users: micOnlineUsers,
      mifapay,
      mifapay_recharge_usd_minor: mifapayMetric,
      new_paid_users: readOptionalNumber(item, "new_paid_users", "newPaidUsers"),
      new_user_recharge_usd_minor: readOptionalNumber(item, "new_user_recharge_usd_minor", "newUserRechargeUsdMinor"),
      new_users: readOptionalNumber(item, "new_users", "newUsers"),
      output_coin: outputCoin,
      paid_users: readOptionalNumber(item, "paid_users", "paidUsers"),
      platform_grant_coin: readOptionalNumber(item, "platform_grant_coin", "platformGrantCoin"),
      recharge_usd_minor: recharge,
      recharge_users: readOptionalNumber(item, "recharge_users", "rechargeUsers", "paid_users", "paidUsers"),
      salary_usd_minor: readOptionalNumber(item, "salary_usd_minor", "salaryUsdMinor"),
      salary_transfer_coin: readOptionalNumber(item, "salary_transfer_coin", "salaryTransferCoin"),
      stat_day: item.stat_day || item.statDay || item.day || item.label || "",
      total: recharge,
      user_recharge_usd_minor: userRecharge,
    };
  });
}

function metricTrend(series, key, name, format = "number") {
  const data = series.map((item) => ({
    label: item.label || item.stat_day || "-",
    value: format === "money" ? numberValue(item[key]) / 100 : numberValue(item[key]),
  }));
  return data.length ? [{ data, name }] : [];
}

function normalizeGiftRanking(source) {
  if (Array.isArray(source.gift_ranking) && source.gift_ranking.length) {
    return source.gift_ranking.map((item) => ({
      name: item.name || item.gift_name || item.gift_id || "-",
      trend_rate: numberValue(item.trend_rate ?? item.trendRate),
      value: numberValue(item.value ?? item.amount ?? item.coin),
    }));
  }
  return [];
}

function normalizeDistribution(source) {
  if (Array.isArray(source.payout_distribution) && source.payout_distribution.length) {
    return source.payout_distribution.map((item) => ({ name: item.name || "-", value: numberValue(item.value) }));
  }
  if (!source || !Object.keys(source).length) {
    return [];
  }
  return [
    { name: "幸运礼物返奖", value: numberValue(source.lucky_gift_payout) },
    { name: "幸运礼物利润", value: Math.max(numberValue(source.lucky_gift_profit), 0) },
  ];
}

function normalizeLuckyGiftPools(source) {
  const raw = firstArray(source?.lucky_gift_pools, source?.luckyGiftPools, source?.lucky_gift_pool_stats, source?.luckyGiftPoolStats, source?.pool_breakdown);
  return raw
    .map((item) => {
      const turnover = readNumber(item, "turnover_coin", "turnoverCoin", "turnover", "total_spent_coins", "totalSpentCoins", "lucky_gift_turnover", "luckyGiftTurnover");
      const payout = readNumber(item, "payout_coin", "payoutCoin", "payout", "total_reward_coins", "totalRewardCoins", "lucky_gift_payout", "luckyGiftPayout");
      const rawProfit = item.profit_coin ?? item.profitCoin ?? item.profit ?? item.lucky_gift_profit ?? item.luckyGiftProfit;
      const profit = rawProfit === undefined || rawProfit === null ? turnover - payout : numberValue(rawProfit);
      return {
        payout,
        payout_rate: numberValue(item.payout_rate ?? item.payoutRate ?? ratio(payout, turnover)),
        pool_id: String(item.pool_id ?? item.poolId ?? item.id ?? "-").trim() || "-",
        profit,
        profit_rate: numberValue(item.profit_rate ?? item.profitRate ?? ratio(profit, turnover)),
        turnover,
      };
    })
    .sort((left, right) => right.turnover - left.turnover || left.pool_id.localeCompare(right.pool_id));
}

function summarizeLuckyGiftPools(items) {
  return items.reduce(
    (summary, item) => ({
      payout: summary.payout + item.payout,
      profit: summary.profit + item.profit,
      turnover: summary.turnover + item.turnover,
    }),
    { payout: 0, profit: 0, turnover: 0 },
  );
}

function firstArray(...values) {
  return values.find((value) => Array.isArray(value)) || [];
}

function normalizeGameRanking(source) {
  if (Array.isArray(source.game_ranking) && source.game_ranking.length) {
    return source.game_ranking.map((item) => ({
      game_id: item.game_id || item.gameId || "-",
      game_label: gameDisplayLabel(item),
      game_name: item.game_name || item.gameName || item.name || "",
      platform_code: item.platform_code || item.platformCode || "",
      profit_rate: numberValue(item.profit_rate ?? item.profitRate),
      turnover_coin: numberValue(item.turnover_coin ?? item.turnoverCoin),
    }));
  }
  return [];
}

function gameDisplayLabel(item) {
  const id = String(item.game_id || item.gameId || "").trim();
  const name = String(item.game_name || item.gameName || item.name || "").trim();
  if (name && id && name !== id) {
    return `${name}（${id}）`;
  }
  return name || id || "-";
}

function localizeSeriesLabel(label) {
  const match = String(label).match(/^May\s+(\d{1,2})$/i);
  if (match) {
    return `5月${match[1]}日`;
  }
  return label;
}
