import CopyOutlined from "@ant-design/icons/es/icons/CopyOutlined";
import DownOutlined from "@ant-design/icons/es/icons/DownOutlined";
import ReloadOutlined from "@ant-design/icons/es/icons/ReloadOutlined";
import SearchOutlined from "@ant-design/icons/es/icons/SearchOutlined";
import Alert from "antd/es/alert";
import Button from "antd/es/button";
import ConfigProvider from "antd/es/config-provider";
import Empty from "antd/es/empty";
import Input from "antd/es/input";
import Pagination from "antd/es/pagination";
import Select from "antd/es/select";
import Spin from "antd/es/spin";
import Tag from "antd/es/tag";
import Tooltip from "antd/es/tooltip";
import { useEffect, useMemo, useState } from "react";
import {
  fetchListingsPage,
  fetchMobileHomeConfig,
  type Listing,
  type ListingPublishOptions,
  type PublicListingQuery,
} from "./api";
import {
  formatCoin,
  formatEstimatedRentalDuration,
  formatListingCode,
  formatListingInfoText,
  formatMoney,
  formatRatio,
  getConsumablePrice,
  getListingDeposit,
  getListingPrice,
  getMainChips,
  getOnlineTimeText,
  getRegions,
  getResourceQuantity,
  getResources,
  getSkinNames,
  readAssetNumber,
  readAssetString,
} from "./listingDisplay";

const pageSize = 10;

const initialOptions: ListingPublishOptions = {
  server_options: [],
  login_method_options: [],
  rank_options: [],
  insurance_options: [],
  level_options: [],
  skin_groups: [],
};

const fallbackLevels = ["4级", "5级", "6级", "7级"];
const fallbackInsurance = ["1*2", "2*2", "2*3", "3*3"];
const fallbackLogins = ["QQ", "微信", "Steam", "不限"];
const fallbackRanks = ["青铜", "白银", "黄金", "铂金", "钻石", "黑鹰", "巅峰"];
const fallbackSkins = ["信条", "坠星者", "处刑者", "影锋", "暗星", "电锯", "蚀金玫瑰", "维什戴尔"];

const coinRanges = [
  { label: "15-50", min: "15", max: "50" },
  { label: "50-100", min: "50", max: "100" },
  { label: "100-150", min: "100", max: "150" },
  { label: "150-200", min: "150", max: "200" },
  { label: "200-250", min: "200", max: "250" },
  { label: "300以上", min: "300", max: "" },
];

const priceRanges = [
  { label: "0-50", min: "0", max: "50" },
  { label: "50-100", min: "50", max: "100" },
  { label: "100-150", min: "100", max: "150" },
  { label: "150-200", min: "150", max: "200" },
  { label: "200以上", min: "200", max: "" },
];

const sortOptions = [
  { key: "recommended", label: "默认排序" },
  { key: "priceDesc", label: "价格排序" },
  { key: "ratioDesc", label: "比例排序" },
  { key: "coinDesc", label: "哈夫币排序" },
  { key: "ratioAsc", label: "绝密KD排序" },
  { key: "coinAsc", label: "AWM排序" },
  { key: "priceAsc", label: "六头排序" },
  { key: "loadDesc", label: "六甲排序" },
];

interface Filters {
  keyword: string;
  minCoin: string;
  maxCoin: string;
  minPrice: string;
  maxPrice: string;
  minKd: string;
  maxKd: string;
  stamina: string[];
  load: string[];
  insurance: string[];
  skinNames: string[];
  loginMethods: string[];
  ranks: string[];
  zones: string[];
}

const emptyFilters: Filters = {
  keyword: "",
  minCoin: "",
  maxCoin: "",
  minPrice: "",
  maxPrice: "",
  minKd: "",
  maxKd: "",
  stamina: [],
  load: [],
  insurance: [],
  skinNames: [],
  loginMethods: [],
  ranks: [],
  zones: [],
};

export default function App() {
  return (
    <ConfigProvider
      theme={{
        token: {
          colorPrimary: "#ff6b00",
          borderRadius: 5,
          fontFamily: 'Inter, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif',
        },
        components: {
          Button: {
            controlHeight: 31,
            borderRadius: 4,
          },
          Input: {
            controlHeight: 38,
          },
          Select: {
            controlHeight: 30,
          },
        },
      }}
    >
      <Showcase />
    </ConfigProvider>
  );
}

function Showcase() {
  const [listings, setListings] = useState<Listing[]>([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [loadError, setLoadError] = useState("");
  const [announcements, setAnnouncements] = useState<string[]>([]);
  const [publishOptions, setPublishOptions] = useState<ListingPublishOptions>(initialOptions);
  const [filters, setFilters] = useState<Filters>(cloneFilters(emptyFilters));
  const [appliedFilters, setAppliedFilters] = useState<Filters>(cloneFilters(emptyFilters));
  const [activeSort, setActiveSort] = useState("recommended");
  const [advancedOpen, setAdvancedOpen] = useState(false);
  const [copiedCode, setCopiedCode] = useState("");
  const [copiedInfoCode, setCopiedInfoCode] = useState("");

  const levelOptions = useMemo(
    () => (publishOptions.level_options.length ? publishOptions.level_options : fallbackLevels),
    [publishOptions.level_options]
  );
  const insuranceOptions = useMemo(
    () => (publishOptions.insurance_options.length ? publishOptions.insurance_options : fallbackInsurance),
    [publishOptions.insurance_options]
  );
  const loginOptions = useMemo(
    () => (publishOptions.login_method_options.length ? publishOptions.login_method_options : fallbackLogins),
    [publishOptions.login_method_options]
  );
  const rankOptions = useMemo(
    () => (publishOptions.rank_options.length ? publishOptions.rank_options : fallbackRanks),
    [publishOptions.rank_options]
  );
  const zoneOptions = useMemo(() => publishOptions.server_options, [publishOptions.server_options]);
  const skinSections = useMemo(() => {
    const groups = publishOptions.skin_groups.filter((group) =>
      ["melee", "operatorGold", "operatorRed", "weapon"].includes(group.key)
    );
    if (groups.length) {
      return groups.map((group) => ({
        key: group.key,
        title: groupTitle(group.key, group.title),
        options: group.options,
      }));
    }
    return [{ key: "skin", title: "稀有皮肤", options: fallbackSkins }];
  }, [publishOptions.skin_groups]);

  const noticeText = announcements[0]?.trim() || "";

  const displayListings = useMemo(() => {
    const items = [...listings];
    if (activeSort === "ratioAsc") return items.sort((a, b) => ratioValue(a) - ratioValue(b));
    if (activeSort === "ratioDesc") return items.sort((a, b) => ratioValue(b) - ratioValue(a));
    if (activeSort === "coinAsc") return items.sort((a, b) => a.haf_coin_amount - b.haf_coin_amount);
    if (activeSort === "loadDesc") {
      return items.sort((a, b) => getResourceQuantity(b, "armor6") - getResourceQuantity(a, "armor6"));
    }
    return items;
  }, [activeSort, listings]);

  useEffect(() => {
    let disposed = false;
    fetchMobileHomeConfig()
      .then((home) => {
        if (disposed) return;
        setAnnouncements(home.announcements);
        setPublishOptions(home.publish_options);
      })
      .catch(() => {
        if (!disposed) setPublishOptions(initialOptions);
      });
    return () => {
      disposed = true;
    };
  }, []);

  useEffect(() => {
    let disposed = false;
    setLoading(true);
    setLoadError("");
    fetchListingsPage(buildQuery(page, appliedFilters, activeSort))
      .then((result) => {
        if (disposed) return;
        setListings(result.items);
        setTotal(result.total);
      })
      .catch((error) => {
        if (disposed) return;
        setListings([]);
        setTotal(0);
        setLoadError(error instanceof Error ? error.message : "加载失败");
      })
      .finally(() => {
        if (!disposed) setLoading(false);
      });
    return () => {
      disposed = true;
    };
  }, [activeSort, appliedFilters, page]);

  function updateFilter<K extends keyof Filters>(key: K, value: Filters[K]) {
    setFilters((current) => ({ ...current, [key]: value }));
  }

  function toggleFilterList(key: "stamina" | "load" | "insurance" | "skinNames", value: string) {
    setFilters((current) => {
      const nextList = current[key].includes(value) ? current[key].filter((item) => item !== value) : [...current[key], value];
      return { ...current, [key]: nextList };
    });
  }

  function applyFilters(nextFilters = filters) {
    setAppliedFilters(cloneFilters(nextFilters));
    setPage(1);
  }

  function resetFilters() {
    const nextFilters = cloneFilters(emptyFilters);
    setFilters(nextFilters);
    setAppliedFilters(cloneFilters(nextFilters));
    setActiveSort("recommended");
    setPage(1);
  }

  function selectRange(minKey: "minCoin" | "minPrice", maxKey: "maxCoin" | "maxPrice", min: string, max: string) {
    setFilters((current) => {
      if (current[minKey] === min && current[maxKey] === max) {
        return { ...current, [minKey]: "", [maxKey]: "" };
      }
      return { ...current, [minKey]: min, [maxKey]: max };
    });
  }

  function setSort(sortKey: string) {
    setActiveSort(sortKey);
    setPage(1);
  }

  async function copyListingCode(item: Listing) {
    const code = formatListingCode(item);
    try {
      await navigator.clipboard?.writeText(code);
      setCopiedCode(code);
      setCopiedInfoCode("");
      window.setTimeout(() => {
        setCopiedCode((current) => (current === code ? "" : current));
      }, 1500);
    } catch {
      setCopiedCode("");
    }
  }

  async function copyListingInfo(item: Listing) {
    const code = formatListingCode(item);
    try {
      await navigator.clipboard?.writeText(formatListingInfoText(item));
      setCopiedInfoCode(code);
      setCopiedCode("");
      window.setTimeout(() => {
        setCopiedInfoCode((current) => (current === code ? "" : current));
      }, 1500);
    } catch {
      setCopiedInfoCode("");
    }
  }

  return (
    <main className="show-page">
      <header className="site-header">
        <div className="nav-inner">
          <div className="brand">
            <span className="brand-mark">锤</span>
            <div>
              <strong>大锤商行</strong>
              <span>哈夫租号</span>
            </div>
          </div>
          <nav className="nav-links">
            <a className="active">资源号租赁</a>
          </nav>
          <div className="nav-actions">
            <span>已实名认证 · 安全交易</span>
            <Button type="primary">24h在线客服</Button>
          </div>
        </div>
        <div className="warning-line" role="note">
          禁止未成年人租号、购买游戏账号，严禁任何用户向未成年人出租、出售账号。
        </div>
      </header>

      <section className="search-stage">
        <Input.Search
          enterButton={<SearchOutlined />}
          onChange={(event) => updateFilter("keyword", event.target.value)}
          onSearch={() => applyFilters()}
          placeholder="请输入编号"
          value={filters.keyword}
        />
      </section>

      {noticeText ? (
        <div className="notice-line" role="status" aria-live="polite">
          <span className="notice-label">公告</span>
          <div className="notice-marquee">
            <div className="marquee-track">
              <span>{noticeText}</span>
              <span aria-hidden="true">{noticeText}</span>
            </div>
          </div>
        </div>
      ) : null}

      <section className="filter-board">
        <div className="section-title">
          <i />
          筛选条件
        </div>
        <div className="filter-grid">
          <FilterRow
            active={(item) => filters.minCoin === item.min && filters.maxCoin === item.max}
            label="哈夫币(M)"
            options={coinRanges}
            render={(item) => item.label}
            onSelect={(item) => selectRange("minCoin", "maxCoin", item.min, item.max)}
          />
          <FilterRow
            active={(item) => filters.minPrice === item.min && filters.maxPrice === item.max}
            label="租金"
            options={priceRanges}
            render={(item) => item.label}
            onSelect={(item) => selectRange("minPrice", "maxPrice", item.min, item.max)}
          />
          <FilterRow
            active={(item) => filters.insurance.includes(item)}
            label="保险"
            options={insuranceOptions}
            render={insuranceLabel}
            onSelect={(item) => toggleFilterList("insurance", item)}
          />
          <FilterRow
            active={(item) => filters.stamina.includes(item)}
            label="体力"
            options={levelOptions}
            render={(item) => item}
            onSelect={(item) => toggleFilterList("stamina", item)}
          />
          <FilterRow
            active={(item) => filters.load.includes(item)}
            label="负重"
            options={levelOptions}
            render={(item) => item}
            onSelect={(item) => toggleFilterList("load", item)}
          />
          <div className="filter-row compact-select">
            <span>IP地址</span>
            <Select
              allowClear
              mode="multiple"
              maxTagCount="responsive"
              options={zoneOptions.map((item) => ({ label: item, value: item }))}
              placeholder="全部"
              value={filters.zones}
              onChange={(value) => updateFilter("zones", value)}
            />
          </div>
          <div className="filter-row compact-select">
            <span>登录方式</span>
            <Select
              allowClear
              mode="multiple"
              maxTagCount="responsive"
              options={loginOptions.map((item) => ({ label: item, value: item }))}
              placeholder="不限"
              value={filters.loginMethods}
              onChange={(value) => updateFilter("loginMethods", value)}
            />
          </div>
          <div className="filter-row compact-select">
            <span>段位</span>
            <Select
              allowClear
              mode="multiple"
              maxTagCount="responsive"
              options={rankOptions.map((item) => ({ label: item, value: item }))}
              placeholder="不限"
              value={filters.ranks}
              onChange={(value) => updateFilter("ranks", value)}
            />
          </div>
        </div>

        <div className={advancedOpen ? "advanced-panel open" : "advanced-panel"}>
          <div className="range-fields">
            <label>
              <span>哈夫币范围</span>
              <div className="range-inputs">
                <Input
                  inputMode="numeric"
                  placeholder="最小"
                  value={filters.minCoin}
                  onChange={(event) => updateFilter("minCoin", event.target.value)}
                />
                <em>至</em>
                <Input
                  inputMode="numeric"
                  placeholder="最大"
                  value={filters.maxCoin}
                  onChange={(event) => updateFilter("maxCoin", event.target.value)}
                />
              </div>
            </label>
            <label>
              <span>KD范围</span>
              <div className="range-inputs">
                <Input
                  inputMode="decimal"
                  placeholder="最小"
                  value={filters.minKd}
                  onChange={(event) => updateFilter("minKd", event.target.value)}
                />
                <em>至</em>
                <Input
                  inputMode="decimal"
                  placeholder="最大"
                  value={filters.maxKd}
                  onChange={(event) => updateFilter("maxKd", event.target.value)}
                />
              </div>
            </label>
          </div>
          {skinSections.map((section) => (
            <FilterRow
              key={section.key}
              active={(item) => filters.skinNames.includes(item)}
              label={section.title}
              options={section.options.slice(0, 18)}
              render={(item) => item}
              onSelect={(item) => toggleFilterList("skinNames", item)}
            />
          ))}
        </div>

        <div className="filter-footer">
          <button className="advanced-toggle" type="button" onClick={() => setAdvancedOpen((open) => !open)}>
            {advancedOpen ? "收起高级筛选" : "展开高级筛选"}
            <DownOutlined rotate={advancedOpen ? 180 : 0} />
          </button>
        </div>
      </section>

      <section className="result-area">
        <div className="sort-toolbar">
          <div className="sort-segment" role="tablist" aria-label="排序方式">
            {sortOptions.map((option) => (
              <button
                key={option.key}
                type="button"
                role="tab"
                aria-selected={activeSort === option.key}
                className={activeSort === option.key ? "active" : ""}
                onClick={() => setSort(option.key)}
              >
                {option.label}
              </button>
            ))}
          </div>
          <div className="toolbar-actions">
            <Button icon={<ReloadOutlined />} onClick={resetFilters}>
              重置
            </Button>
            <Button icon={<SearchOutlined />} type="primary" onClick={() => applyFilters()}>
              搜索
            </Button>
          </div>
        </div>

        {loadError ? <Alert showIcon type="error" message={loadError} action={<Button onClick={() => applyFilters()}>重试</Button>} /> : null}

        <Spin spinning={loading}>
          {displayListings.length ? (
            <div className="listing-list">
              {displayListings.map((item) => (
                <ListingRow
                  key={item.id}
                  codeCopied={copiedCode === formatListingCode(item)}
                  infoCopied={copiedInfoCode === formatListingCode(item)}
                  item={item}
                  onCopyCode={copyListingCode}
                  onCopyInfo={copyListingInfo}
                />
              ))}
            </div>
          ) : (
            <Empty className="empty-state" description={loading ? "正在加载账号" : "暂无符合条件的账号"} />
          )}
        </Spin>

        <div className="pagination-bar">
          <Pagination
            current={page}
            pageSize={pageSize}
            showSizeChanger={false}
            showTotal={(count) => `第 ${page} 页 / 共 ${Math.max(1, Math.ceil(count / pageSize))} 页`}
            total={total}
            onChange={setPage}
          />
        </div>
      </section>

      <footer className="site-footer">
        <div className="footer-inner">
          <strong>大锤商行</strong>
          <span className="footer-divider" aria-hidden="true">
            ·
          </span>
          <span>版权所有 ©2026 大锤网络游戏有限公司</span>
          <span className="footer-divider" aria-hidden="true">
            ·
          </span>
          <a href="https://beian.miit.gov.cn/" target="_blank" rel="noreferrer">
            湘ICP备2025146668号
          </a>
        </div>
      </footer>
    </main>
  );
}

interface FilterRowProps<T> {
  label: string;
  options: T[];
  active: (item: T) => boolean;
  render: (item: T) => string;
  onSelect: (item: T) => void;
}

function FilterRow<T>({ active, label, onSelect, options, render }: FilterRowProps<T>) {
  return (
    <div className="filter-row">
      <span className="filter-label">{label}</span>
      <div className="chip-list">
        {options.map((item) => {
          const text = render(item);
          const selected = active(item);
          return (
            <button
              key={text}
              className={selected ? "selected" : ""}
              type="button"
              aria-pressed={selected}
              onClick={() => onSelect(item)}
            >
              {text}
            </button>
          );
        })}
      </div>
    </div>
  );
}

interface ListingRowProps {
  item: Listing;
  codeCopied: boolean;
  infoCopied: boolean;
  onCopyCode: (item: Listing) => void;
  onCopyInfo: (item: Listing) => void;
}

const FIXED_LISTING_COVER = "/listing-cover.jpg";

function ListingRow({ codeCopied, infoCopied, item, onCopyCode, onCopyInfo }: ListingRowProps) {
  const code = formatListingCode(item);
  const price = getListingPrice(item);
  const deposit = getListingDeposit(item);
  const consumable = getConsumablePrice(item);
  const totalPrice = Math.round((price + deposit) * 100) / 100;
  const rentalDuration = formatEstimatedRentalDuration(item);
  const remark = getAssetText(item, "remark");
  const regions = getRegions(item);
  const skinNames = getSkinNames(item);
  const fireLevel = readAssetNumber(item, "fire_level");
  const resources = getResources(item);
  const resourceTooltip = (
    <ResourceTooltipContent consumable={consumable} resources={resources} />
  );
  const details = [
    ["哈夫币", formatCoin(item)],
    ["保险格", readAssetString(item, "season_insurance") || "-"],
    ["体力", readAssetString(item, "stamina_level") || "-"],
    ["负重", readAssetString(item, "load_level") || "-"],
    ["六头", String(getResourceQuantity(item, "helmet6") || "-")],
    ["六甲", String(getResourceQuantity(item, "armor6") || "-")],
    ["AW子弹", String(getResourceQuantity(item, "awmAmmo") || "-")],
    ["绝密KD", String(readAssetNumber(item, "secret_kd") || "0.00")],
    ["比例", formatRatio(item)],
    ["红皮", skinNames.slice(0, 2).join("、") || "-"],
    ["登录方式", item.login_platform || "-"],
    ["IP", regions[0] || item.server_region || "-"],
  ];

  return (
    <article className="listing-row">
      <div className="listing-cover">
        <img alt={item.title || code} decoding="async" loading="lazy" src={FIXED_LISTING_COVER} />
      </div>
      <div className="listing-main">
        <div className="listing-head">
          <div>
            <strong>{item.title || `纯币${formatCoin(item)}资源号`}</strong>
            <Tag color="orange">{code}</Tag>
            <Button icon={<CopyOutlined />} size="small" type="text" onClick={() => onCopyCode(item)}>
              {codeCopied ? "已复制" : "复制"}
            </Button>
          </div>
          <Tag color={item.is_accelerated_sale ? "orange" : "gold"}>{item.is_accelerated_sale ? "爆卖" : item.rank_level || "展示"}</Tag>
        </div>

        <div className="detail-grid">
          {details.map(([label, value]) => (
            <div key={`${code}-${label}`} className="detail-item">
              <span>{label}:</span>
              <strong>{value}</strong>
            </div>
          ))}
        </div>

        <div className="listing-meta">
          {getOnlineTimeText(item) ? <Tag color="cyan">{getOnlineTimeText(item)}</Tag> : null}
          {fireLevel ? <Tag color="volcano">烽火 {fireLevel}</Tag> : null}
          {getMainChips(item)
            .slice(0, 5)
            .map((chip) => (
              <Tag key={`${code}-${chip.label}`} className={`mini-tag tag-${chip.tone}`}>
                {chip.label}
              </Tag>
            ))}
          {resources.length ? <Tag color="orange">额外{resources.length}项</Tag> : null}
          {remark ? <span className="remark">备注：{remark}</span> : null}
        </div>

        <div className="price-line">
          <span>
            租金(包含额外物品) <strong>￥{formatMoney(price)}</strong>
          </span>
          <Tooltip
            arrow={false}
            placement="top"
            rootClassName="resource-tooltip-root"
            styles={{
              container: {
                padding: 0,
                background: "#fff",
                color: "#172033",
                borderRadius: 12,
                boxShadow: "0 12px 32px rgba(21, 32, 43, 0.14)",
              },
            }}
            title={resourceTooltip}
          >
            <span className="price-hint">
              额外消耗品 <strong>￥{formatMoney(consumable)}</strong>
            </span>
          </Tooltip>
          <span>
            租期 <strong className="rental-days">{rentalDuration}</strong>
          </span>
          <span>
            押金 <strong>￥{formatMoney(deposit)}</strong>
          </span>
          <span>
            合计 <strong>￥{formatMoney(totalPrice)}</strong>
          </span>
          <Button type="link" onClick={() => onCopyInfo(item)}>
            {infoCopied ? "已复制" : "复制信息"}
          </Button>
        </div>
      </div>
    </article>
  );
}

function ResourceTooltipContent({
  consumable,
  resources,
}: {
  consumable: number;
  resources: ReturnType<typeof getResources>;
}) {
  if (!resources.length) {
    return <div className="resource-popover empty">暂无额外物品</div>;
  }

  return (
    <div className="resource-popover">
      <div className="resource-popover-head">
        <div>
          <strong>额外物品明细</strong>
          <span>{resources.length} 项</span>
        </div>
        <em>合计 ￥{formatMoney(consumable)}</em>
      </div>
      <div className="resource-popover-list">
        {resources.map((resource) => (
          <div key={resource.key} className="resource-popover-row">
            <div className="resource-popover-main">
              <span className="resource-name">{resource.label}</span>
              <span className="resource-qty">×{resource.quantity}</span>
              <span className={resource.mode === "收费" ? "resource-mode paid" : "resource-mode gift"}>
                {resource.mode || "-"}
              </span>
            </div>
            <div className="resource-popover-side">
              <span className="resource-unit">{resource.price || "-"}</span>
              <strong className="resource-amount">
                {resource.mode === "收费" && resource.amount > 0 ? `￥${formatMoney(resource.amount)}` : "—"}
              </strong>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function buildQuery(nextPage: number, filters: Filters, sort: string): PublicListingQuery {
  return {
    page: nextPage,
    page_size: pageSize,
    keyword: filters.keyword.trim(),
    sort: backendSortKey(sort),
    zone: filters.zones.join(","),
    login_method: filters.loginMethods.join(","),
    rank: filters.ranks.join(","),
    min_coin: toNumber(filters.minCoin),
    max_coin: toNumber(filters.maxCoin),
    min_price: toNumber(filters.minPrice),
    max_price: toNumber(filters.maxPrice),
    min_secret_kd: toNumber(filters.minKd),
    max_secret_kd: toNumber(filters.maxKd),
    stamina: filters.stamina.join(","),
    load: filters.load.join(","),
    insurance: filters.insurance.join(","),
    skin_name: filters.skinNames.join(","),
  };
}

function backendSortKey(sort: string) {
  if (sort === "priceAsc" || sort === "priceDesc" || sort === "coinDesc") return sort;
  return "recommended";
}

function ratioValue(item: Listing) {
  const configuredRatio = readAssetNumber(item, "publish_ratio");
  if (configuredRatio > 0) return configuredRatio;
  const price = getListingPrice(item);
  if (!price) return 0;
  return Number(item.haf_coin_amount || 0) / 10000 / price;
}

function toNumber(value: string) {
  if (!value.trim()) return undefined;
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : undefined;
}

function cloneFilters(filters: Filters): Filters {
  return {
    ...filters,
    stamina: [...filters.stamina],
    load: [...filters.load],
    insurance: [...filters.insurance],
    skinNames: [...filters.skinNames],
    loginMethods: [...filters.loginMethods],
    ranks: [...filters.ranks],
    zones: [...filters.zones],
  };
}

function groupTitle(key: string, title: string) {
  if (key === "melee") return "刀皮";
  if (key === "operatorGold") return "干员金皮";
  if (key === "operatorRed") return "干员红皮";
  if (key === "weapon") return "枪皮";
  return title;
}

function insuranceLabel(value: string) {
  const [rows, cols] = value.split("*").map((item) => Number(item));
  if (rows > 0 && cols > 0) return `${rows * cols}格`;
  return value;
}

function getAssetText(item: Listing, key: string) {
  const value = item.asset_summary?.[key];
  return typeof value === "string" ? value : "";
}
