<script setup lang="ts">
import { ref, computed, onMounted, defineAsyncComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { formatApiErrorMessage, isApiErrorCode } from '@thebet365/shared';
import api from '../api';
import { ElMessage } from 'element-plus';
import { useAdminLocale } from '../composables/useAdminLocale';
import { usePermissions } from '../composables/usePermissions';
import { AdminPerm } from '../constants/permissions';

const VChart = defineAsyncComponent(() =>
  import('../components/dashboard/echarts-setup').then((m) => m.VChart),
);
import { formatAmount } from '../utils/format-amount';
import {
  buildBetTypePieOption,
  buildSelectionStakeBarOption,
  buildStatusPieOption,
} from '../utils/settlement-charts';
import {
  betStatusLabel,
  betStatusTagType,
  betTypeLabel,
  betResultLabel,
} from '../utils/bet-labels';
import { adminSelectionLabel } from '../utils/adminSelectionLabel';
import type { AdminMatchDetail } from './match-form';
import AdminSubNav from '../components/AdminSubNav.vue';

interface SettlementBetStats {
  summary: {
    totalBets: number;
    singleBets: number;
    parlayBets: number;
    totalStake: string;
    totalPotentialReturn: string;
    statusCounts: Record<string, number>;
    legCount: number;
  };
  bySelection: Array<{
    marketType: string;
    period: string | null;
    selectionName: string;
    selectionId: string;
    legCount: number;
    singleStake: string;
    parlayLegCount: number;
  }>;
  bets: {
    items: Array<{
      id: string;
      betNo: string;
      username: string;
      betType: string;
      status: string;
      stake: string;
      potentialReturn: string | null;
      placedAt: string;
      legCountOnMatch: number;
      selections: Array<{
        marketType: string;
        period: string | null;
        selectionName: string;
        odds: string;
      }>;
    }>;
    total: number;
    page: number;
    pageSize: number;
  };
}

const { t, locale, localeTag } = useAdminLocale();
const { hasPermission } = usePermissions();
const canResettle = computed(() => hasPermission(AdminPerm.resettle));
const route = useRoute();
const router = useRouter();
const STAT_FACT_LABELS = {
  homeCorners: {
    'zh-CN': '主队角球',
    'en-US': 'Home corners',
    'ms-MY': 'Sepakan sudut tuan rumah',
  },
  awayCorners: {
    'zh-CN': '客队角球',
    'en-US': 'Away corners',
    'ms-MY': 'Sepakan sudut pelawat',
  },
  homeCards: {
    'zh-CN': '主队黄牌/红牌',
    'en-US': 'Home yellow/red cards',
    'ms-MY': 'Kad kuning/merah tuan rumah',
  },
  awayCards: {
    'zh-CN': '客队黄牌/红牌',
    'en-US': 'Away yellow/red cards',
    'ms-MY': 'Kad kuning/merah pelawat',
  },
} as const;

const loading = ref(false);
const previewing = ref(false);
const match = ref<AdminMatchDetail | null>(null);
const score = ref({ htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 });
const matchStats = ref<{
  homeCorners: number | null;
  awayCorners: number | null;
  homeYellowCards: number | null;
  awayYellowCards: number | null;
  homeRedCards: number | null;
  awayRedCards: number | null;
  homeCards: number | null;
  awayCards: number | null;
}>(emptyMatchStats());

function emptyMatchStats() {
  return {
    homeCorners: null,
    awayCorners: null,
    homeYellowCards: null,
    awayYellowCards: null,
    homeRedCards: null,
    awayRedCards: null,
    homeCards: null,
    awayCards: null,
  };
}

function cardTotalFromBreakdown(
  yellow: number | null,
  red: number | null,
  fallback: number | null,
) {
  const hasBreakdown = yellow != null || red != null;
  if (!hasBreakdown) return fallback ?? null;
  if (yellow == null || red == null) return null;
  return yellow + red;
}

function settlementStatsPayload() {
  const s = matchStats.value;
  return {
    ...s,
    homeCards: cardTotalFromBreakdown(s.homeYellowCards, s.homeRedCards, s.homeCards),
    awayCards: cardTotalFromBreakdown(s.awayYellowCards, s.awayRedCards, s.awayCards),
  };
}

function resetSettlementInputs() {
  score.value = { htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 };
  matchStats.value = emptyMatchStats();
}

const winnerTeamId = ref('');
const outrightSelections = ref<
  Array<{ teamId: string; teamCode: string; teamZh: string; teamEn: string }>
>([]);
const preview = ref<Record<string, unknown> | null>(null);
const resettlePreview = ref<Record<string, unknown> | null>(null);
const resettleReason = ref('');
const statsSummary = ref<Pick<SettlementBetStats, 'summary' | 'bySelection'> | null>(null);
const betsList = ref<SettlementBetStats['bets'] | null>(null);
const statsLoading = ref(false);
const betsLoading = ref(false);

const stats = computed((): SettlementBetStats | null => {
  if (!statsSummary.value || !betsList.value) return null;
  return { ...statsSummary.value, bets: betsList.value };
});
const betPage = ref(1);
const betPageSize = ref(10);
const previewPage = ref(1);
const previewPageSize = ref(10);

// 智能比分推荐已暂时关闭（后端 smart-score.solver.ts 保留，恢复时接回 UI 与 POST /settlement/smart-score）

const matchId = computed(() => String(route.params.id ?? ''));
const isOutright = computed(() => match.value?.isOutright === true);

const outrightTitle = computed(() => {
  const m = match.value;
  if (!m) return '';
  const name = m.matchName?.trim();
  if (name) return name;
  if (locale.value === 'en-US') return m.leagueEn || m.leagueZh;
  if (locale.value === 'ms-MY') return m.leagueMs || m.leagueEn || m.leagueZh;
  return m.leagueZh || m.leagueEn;
});

const settlementPageTitle = computed(() =>
  isOutright.value ? t('settlement.outright.page_title') : t('page.settlement.title'),
);

function outrightTeamLabel(row: { teamZh: string; teamEn: string; teamCode: string }) {
  const name =
    locale.value === 'en-US'
      ? row.teamEn || row.teamZh
      : locale.value === 'ms-MY'
        ? row.teamEn || row.teamZh
        : row.teamZh || row.teamEn;
  return `${name} (${row.teamCode})`;
}

function buildSettlementPayload(): Record<string, unknown> | null {
  if (isOutright.value) {
    if (!winnerTeamId.value) {
      ElMessage.warning(t('settlement.outright.winner_required'));
      return null;
    }
    return { winnerTeamId: Number(winnerTeamId.value) };
  }
  return { ...score.value, ...settlementStatsPayload() };
}
type PreviewItem = {
  betNo: string;
  betType: string;
  result: string;
  payout: string;
  note?: string;
};

type PreviewItemsPage = {
  items: PreviewItem[];
  total: number;
  page: number;
  pageSize: number;
};

const previewItemsPage = computed(
  () => (preview.value?.items as PreviewItemsPage | undefined) ?? { items: [], total: 0, page: 1, pageSize: 10 },
);

const previewZeroHint = computed(() => {
  const p = preview.value;
  if (!p) return '';
  const payout = Number(p.totalPayout ?? 0);
  const refund = Number(p.totalRefund ?? 0);
  if (payout > 0 || refund > 0) return '';
  const pending = Number(p.pendingOtherMatches ?? 0);
  const wonLegs = Number(p.wonLegsOnMatch ?? 0);
  if (pending > 0) {
    return t('settlement.preview_zero_parlay_hint', { pending: String(pending), legs: String(wonLegs) });
  }
  if (Number(p.lostOnThisMatch ?? 0) > 0) {
    return t('settlement.preview_zero_lost_hint', { count: String(p.lostOnThisMatch) });
  }
  return t('settlement.preview_zero_default_hint');
});

function previewResultLabel(result: string) {
  const key = `settlement.preview.result.${result}`;
  const label = t(key);
  return label !== key ? label : betResultLabel(result);
}

const betTypeChartOption = computed(() => {
  const s = stats.value?.summary;
  if (!s) return null;
  return buildBetTypePieOption({
    title: t('settlement.chart.bet_type'),
    singleBets: s.singleBets,
    parlayBets: s.parlayBets,
    totalBets: s.totalBets,
    legCount: s.legCount,
    labels: {
      single: t('settlement.stats_single'),
      parlay: t('settlement.stats_parlay'),
    },
    subtext: `${s.singleBets} ${t('settlement.stats_single')} · ${s.parlayBets} ${t('settlement.stats_parlay')} · ${s.legCount} ${t('settlement.col.legs')}`,
    centerLabel: t('settlement.stats_total_bets'),
  });
});

const statusChartOption = computed(() => {
  const s = stats.value?.summary;
  if (!s) return null;
  const parts = ['PENDING', 'WON', 'LOST', 'VOID', 'PUSH']
    .filter((st) => (s.statusCounts[st] ?? 0) > 0)
    .map((st) => `${betStatusLabel(st)} ${s.statusCounts[st]}`);
  return buildStatusPieOption({
    title: t('settlement.chart.status'),
    statusCounts: s.statusCounts,
    labelFor: (st) => betStatusLabel(st),
    subtext: parts.join(' · ') || t('settlement.no_bets'),
  });
});

const selectionStakeChartOption = computed(() => {
  const s = stats.value?.summary;
  const rows = stats.value?.bySelection ?? [];
  if (!s) return null;
  const top = [...rows]
    .sort((a, b) => Number(b.singleStake) - Number(a.singleStake))
    .slice(0, 6);
  const labels = top.map((row) => {
    const name = selectionDisplay(row);
    return name.length > 8 ? `${name.slice(0, 8)}…` : name;
  });
  return buildSelectionStakeBarOption({
    title: t('settlement.chart.stake_by_selection'),
    subtext: `${t('settlement.stats_total_stake')} ${formatAmount(s.totalStake)} · ${t('settlement.stats_potential')} ${formatAmount(s.totalPotentialReturn)}`,
    labels: labels.length ? labels : [t('settlement.no_bets')],
    stakes: top.length ? top.map((row) => Number(row.singleStake)) : [0],
    seriesName: t('settlement.col.single_stake'),
  });
});

const leagueLabel = computed(() => {
  const m = match.value;
  if (!m) return '';
  if (locale.value === 'en-US') return m.leagueEn || m.leagueZh;
  if (locale.value === 'ms-MY') return m.leagueMs || m.leagueEn || m.leagueZh;
  return m.leagueZh || m.leagueEn;
});

const homeLabel = computed(() => {
  const m = match.value;
  if (!m) return '';
  if (locale.value === 'en-US') return m.homeTeamEn || m.homeTeamZh;
  if (locale.value === 'ms-MY') return m.homeTeamMs || m.homeTeamEn || m.homeTeamZh;
  return m.homeTeamZh || m.homeTeamEn;
});

const awayLabel = computed(() => {
  const m = match.value;
  if (!m) return '';
  if (locale.value === 'en-US') return m.awayTeamEn || m.awayTeamZh;
  if (locale.value === 'ms-MY') return m.awayTeamMs || m.awayTeamEn || m.awayTeamZh;
  return m.awayTeamZh || m.awayTeamEn;
});

const kickoffLabel = computed(() => {
  const iso = match.value?.startTime;
  if (!iso) return '—';
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return iso;
  return d.toLocaleString(locale.value === 'en-US' ? 'en-GB' : 'zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  });
});

const statusLabel = computed(() => {
  const s = match.value?.status;
  if (!s) return '—';
  const key = `match.status.${s}`;
  const translated = t(key);
  return translated === key ? s : translated;
});

function marketLabel(marketType: string) {
  const key = `matchEditor.market.${marketType}`;
  const label = t(key);
  return label === key ? marketType : label;
}

function selectionDisplay(row: { selectionName: string; period?: string | null }) {
  return adminSelectionLabel(t, row.selectionName, {
    period: row.period ?? undefined,
  });
}

function formatTime(v: string) {
  return new Date(v).toLocaleString(localeTag.value, {
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  });
}

function matchBetSelectionSummary(
  row: SettlementBetStats['bets']['items'][number],
) {
  return row.selections
    .map((leg) => {
      const market = marketLabel(leg.marketType);
      const sel = selectionDisplay(leg);
      return `${market} ${sel} @${leg.odds}`;
    })
    .join(' · ');
}

async function loadStatsSummary() {
  if (!matchId.value) return;
  statsLoading.value = true;
  try {
    const { data } = await api.get(`/admin/matches/${matchId.value}/settlement/summary`);
    statsSummary.value = data.data as Pick<SettlementBetStats, 'summary' | 'bySelection'>;
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.load_failed'));
  } finally {
    statsLoading.value = false;
  }
}

async function loadBets() {
  if (!matchId.value) return;
  betsLoading.value = true;
  try {
    const { data } = await api.get(`/admin/matches/${matchId.value}/settlement/bets`, {
      params: { page: betPage.value, pageSize: betPageSize.value },
    });
    const payload = data.data as SettlementBetStats['bets'];
    betsList.value = payload;
    betPage.value = payload.page;
    betPageSize.value = payload.pageSize;
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.load_failed'));
  } finally {
    betsLoading.value = false;
  }
}

async function loadStats() {
  betPage.value = 1;
  await Promise.all([loadStatsSummary(), loadBets()]);
}

function onBetPageChange(page: number) {
  betPage.value = page;
  void loadBets();
}

function onBetPageSizeChange(size: number) {
  betPageSize.value = size;
  betPage.value = 1;
  void loadBets();
}

async function loadMatch() {
  if (!matchId.value) return;
  loading.value = true;
  try {
    const { data } = await api.get(`/admin/matches/${matchId.value}`);
    const detail = data.data as AdminMatchDetail;
    const settleable =
      detail.status === 'CLOSED' ||
      detail.status === 'PENDING_SETTLEMENT' ||
      detail.status === 'SETTLED';
    if (!settleable) {
      ElMessage.warning(t('settlement.must_close_first'));
      router.replace(detail.isOutright ? '/matches/outrights' : '/matches');
      return;
    }
    match.value = detail;
    if (detail.score) {
      score.value = {
        htHome: detail.score.htHome,
        htAway: detail.score.htAway,
        ftHome: detail.score.ftHome,
        ftAway: detail.score.ftAway,
      };
      matchStats.value = {
        homeCorners: detail.score.homeCorners ?? null,
        awayCorners: detail.score.awayCorners ?? null,
        homeYellowCards: detail.score.homeYellowCards ?? null,
        awayYellowCards: detail.score.awayYellowCards ?? null,
        homeRedCards: detail.score.homeRedCards ?? null,
        awayRedCards: detail.score.awayRedCards ?? null,
        homeCards: detail.score.homeCards ?? null,
        awayCards: detail.score.awayCards ?? null,
      };
      winnerTeamId.value = detail.score.winnerTeamId ?? '';
    } else {
      resetSettlementInputs();
      winnerTeamId.value = '';
    }
    if (detail.isOutright) {
      const outrightRes = await api.get(`/admin/outrights/${matchId.value}`);
      const payload = outrightRes.data.data as {
        selections: Array<{
          teamId: string | null;
          teamCode: string;
          teamZh: string;
          teamEn: string;
          status: string;
        }>;
      };
      outrightSelections.value = (payload.selections ?? [])
        .filter((s) => s.teamId)
        .map((s) => ({
          teamId: s.teamId as string,
          teamCode: s.teamCode,
          teamZh: s.teamZh,
          teamEn: s.teamEn,
        }));
    } else {
      outrightSelections.value = [];
    }
    betPage.value = 1;
    await loadStats();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.load_failed'));
  } finally {
    loading.value = false;
  }
}

const isSettled = computed(() => match.value?.status === 'SETTLED');

async function previewResettlement() {
  const payload = buildSettlementPayload();
  if (!payload) return;
  const { data } = await api.post(`/admin/matches/${matchId.value}/resettle/preview`, {
    ...payload,
    reason: resettleReason.value.trim() || undefined,
  });
  resettlePreview.value = data.data;
}

async function confirmResettle() {
  if (!resettlePreview.value?.batch) return;
  await api.post(`/admin/resettle/${(resettlePreview.value.batch as { id: string }).id}/confirm`);
  ElMessage.success(t('msg.resettle_confirmed'));
  resettlePreview.value = null;
  await loadMatch();
}

function settlementApiError(e: unknown, fallback: string) {
  const err = e as { response?: { data?: { code?: unknown; params?: Record<string, string | number>; error?: string; message?: string } } };
  const code = err.response?.data?.code;
  if (isApiErrorCode(code)) {
    const params = { ...(err.response?.data?.params ?? {}) };
    if (code === 'SETTLEMENT_FACTS_REQUIRED' && typeof params.fields === 'string') {
      params.fields = params.fields
        .split(',')
        .map((field) => {
          const labels = STAT_FACT_LABELS[field as keyof typeof STAT_FACT_LABELS];
          return labels?.[locale.value as keyof typeof labels] ?? field;
        })
        .join(locale.value === 'zh-CN' ? '、' : ', ');
    }
    return formatApiErrorMessage(code, locale.value, params);
  }
  const raw = err.response?.data?.error ?? err.response?.data?.message ?? fallback;
  if (raw === 'Score not recorded' || raw === 'Score not found') {
    return t('settlement.err_score_not_recorded');
  }
  if (raw === 'SETTLEMENT_WINNER_REQUIRED') {
    return t('settlement.outright.winner_required');
  }
  return raw;
}

async function loadPreviewItems(page = previewPage.value, pageSize = previewPageSize.value) {
  const batch = preview.value?.batch as { id: string } | undefined;
  if (!batch) return;
  try {
    const { data } = await api.get(`/admin/settlement/${batch.id}/preview-items`, {
      params: { page, pageSize },
    });
    if (preview.value) {
      preview.value = { ...preview.value, items: data.data as PreviewItemsPage };
    }
    previewPage.value = (data.data as PreviewItemsPage).page;
    previewPageSize.value = (data.data as PreviewItemsPage).pageSize;
  } catch (e: unknown) {
    ElMessage.error(settlementApiError(e, t('settlement.preview_failed')));
  }
}

async function previewSettlement() {
  const payload = buildSettlementPayload();
  if (!payload) return;
  preview.value = null;
  previewPage.value = 1;
  previewing.value = true;
  try {
    const { data } = await api.post(`/admin/matches/${matchId.value}/settlement/preview`, {
      ...payload,
      page: 1,
      pageSize: previewPageSize.value,
    });
    preview.value = data.data;
    const itemsPage = data.data.items as PreviewItemsPage;
    previewPage.value = itemsPage.page;
    previewPageSize.value = itemsPage.pageSize;
    await loadMatch();
  } catch (e: unknown) {
    ElMessage.error(settlementApiError(e, t('settlement.preview_failed')));
  } finally {
    previewing.value = false;
  }
}

function onPreviewPageChange(page: number) {
  previewPage.value = page;
  void loadPreviewItems(page, previewPageSize.value);
}

function onPreviewPageSizeChange(size: number) {
  previewPageSize.value = size;
  previewPage.value = 1;
  void loadPreviewItems(1, size);
}

async function confirm() {
  if (!preview.value?.batch) return;
  await api.post(`/admin/settlement/${(preview.value.batch as { id: string }).id}/confirm`);
  ElMessage.success(t('msg.settlement_confirmed'));
  preview.value = null;
  await loadMatch();
}

onMounted(() => {
  void loadMatch();
});
</script>

<template>
  <div v-loading="loading" class="settlement-page">
    <AdminSubNav
      :title="settlementPageTitle"
      :subtitle="`#${matchId}`"
    >
      <template #extra>
        <el-tag v-if="match" size="small" type="info">{{ statusLabel }}</el-tag>
      </template>
    </AdminSubNav>

    <el-card v-if="match" class="settle-top-card" shadow="never">
      <p v-if="leagueLabel" class="match-league">{{ leagueLabel }}</p>
      <div v-if="isOutright" class="outright-settle-head">
        <p class="outright-settle-title">{{ outrightTitle }}</p>
        <span class="kickoff-inline">
          <span class="meta-k">{{ t('settlement.outright.title') }}</span>
        </span>
      </div>
      <div v-else class="match-inline">
        <div class="team-chip">
          <img
            v-if="match.homeTeamLogoUrl"
            :src="match.homeTeamLogoUrl"
            alt=""
            class="team-logo-sm"
            loading="lazy"
          />
          <span class="team-name-inline">{{ homeLabel }}</span>
        </div>
        <span class="vs">VS</span>
        <div class="team-chip">
          <img
            v-if="match.awayTeamLogoUrl"
            :src="match.awayTeamLogoUrl"
            alt=""
            class="team-logo-sm"
            loading="lazy"
          />
          <span class="team-name-inline">{{ awayLabel }}</span>
        </div>
        <span class="kickoff-inline">
          <span class="meta-k">{{ t('settlement.kickoff') }}</span>
          {{ kickoffLabel }}
        </span>
      </div>

      <div class="settle-score-row">
        <div v-if="isOutright" class="outright-winner-row">
          <span class="score-title">{{ t('settlement.outright.winner') }}</span>
          <el-select
            v-model="winnerTeamId"
            filterable
            clearable
            class="outright-winner-select"
            :placeholder="t('settlement.outright.winner_ph')"
          >
            <el-option
              v-for="row in outrightSelections"
              :key="row.teamId"
              :label="outrightTeamLabel(row)"
              :value="row.teamId"
            />
          </el-select>
        </div>
        <div v-else class="score-inline-group">
          <div class="score-block compact">
            <span class="score-title">{{ t('settlement.ht_score') }}</span>
            <div class="score-inputs">
              <el-input-number v-model="score.htHome" :min="0" controls-position="right" style="width: 88px" />
              <span class="score-sep">—</span>
              <el-input-number v-model="score.htAway" :min="0" controls-position="right" style="width: 88px" />
            </div>
          </div>
          <div class="score-block compact">
            <span class="score-title">{{ t('settlement.ft_score') }}</span>
            <div class="score-inputs">
              <el-input-number v-model="score.ftHome" :min="0" controls-position="right" style="width: 88px" />
              <span class="score-sep">—</span>
              <el-input-number v-model="score.ftAway" :min="0" controls-position="right" style="width: 88px" />
            </div>
          </div>
          <div class="score-block compact stat-block">
            <span class="score-title">{{ t('settlement.corners') }}</span>
            <div class="stat-inputs">
              <span class="stat-side">{{ t('settlement.home_stat') }}</span>
              <el-input-number
                v-model="matchStats.homeCorners"
                :min="0"
                :precision="0"
                controls-position="right"
                style="width: 80px"
              />
              <span class="stat-side">{{ t('settlement.away_stat') }}</span>
              <el-input-number
                v-model="matchStats.awayCorners"
                :min="0"
                :precision="0"
                controls-position="right"
                style="width: 80px"
              />
            </div>
          </div>
          <div class="score-block compact stat-block">
            <span class="score-title">{{ t('settlement.yellow_cards') }}</span>
            <div class="stat-inputs">
              <span class="stat-side">{{ t('settlement.home_stat') }}</span>
              <el-input-number
                v-model="matchStats.homeYellowCards"
                :min="0"
                :precision="0"
                controls-position="right"
                style="width: 80px"
              />
              <span class="stat-side">{{ t('settlement.away_stat') }}</span>
              <el-input-number
                v-model="matchStats.awayYellowCards"
                :min="0"
                :precision="0"
                controls-position="right"
                style="width: 80px"
              />
            </div>
          </div>
          <div class="score-block compact stat-block">
            <span class="score-title">{{ t('settlement.red_cards') }}</span>
            <div class="stat-inputs">
              <span class="stat-side">{{ t('settlement.home_stat') }}</span>
              <el-input-number
                v-model="matchStats.homeRedCards"
                :min="0"
                :precision="0"
                controls-position="right"
                style="width: 80px"
              />
              <span class="stat-side">{{ t('settlement.away_stat') }}</span>
              <el-input-number
                v-model="matchStats.awayRedCards"
                :min="0"
                :precision="0"
                controls-position="right"
                style="width: 80px"
              />
            </div>
          </div>
        </div>

        <div class="settle-actions">
          <template v-if="!isSettled">
            <el-button
              type="primary"
              :loading="previewing"
              @click="previewSettlement"
            >
              {{ t('settlement.preview_btn') }}
            </el-button>
            <span class="preview-hint">{{
              isOutright ? t('settlement.outright.preview_hint') : t('settlement.preview_hint')
            }}</span>
          </template>
          <template v-else-if="canResettle">
            <el-input
              v-model="resettleReason"
              size="small"
              :placeholder="t('settlement.resettle_reason')"
              class="settle-resettle-reason"
            />
            <el-button type="warning" plain @click="previewResettlement">
              {{ t('settlement.resettle_preview') }}
            </el-button>
          </template>
        </div>
      </div>
    </el-card>

    <!-- 智能比分弹窗已关闭（见 Settlement.vue git 历史） -->

    <el-card v-if="canResettle && resettlePreview" class="preview-card" shadow="never">
      <div class="preview-title">{{ t('settlement.resettle_preview_title') }}</div>
      <el-row :gutter="20">
        <el-col :span="8">
          <div class="pstat">
            <div class="pstat-value">{{ resettlePreview.affectedCount }}</div>
            <div class="pstat-label">{{ t('settlement.resettle_affected') }}</div>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="pstat">
            <div class="pstat-value pstat-green">{{ resettlePreview.totalTopup }}</div>
            <div class="pstat-label">{{ t('settlement.resettle_topup') }}</div>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="pstat">
            <div class="pstat-value pstat-orange">{{ resettlePreview.totalClawback }}</div>
            <div class="pstat-label">{{ t('settlement.resettle_clawback') }}</div>
          </div>
        </el-col>
      </el-row>
      <el-button type="warning" class="confirm-btn" @click="confirmResettle">
        {{ t('settlement.resettle_confirm') }}
      </el-button>
    </el-card>

    <el-card v-if="preview" class="preview-card preview-card--compact" shadow="never">
      <div class="preview-bar">
        <span class="preview-bar-title">{{ t('settlement.preview_title') }}</span>
        <div class="preview-metrics">
          <div class="preview-metric">
            <span class="preview-metric-value">{{ preview.pendingBetCount ?? preview.singleBetCount }}</span>
            <span class="preview-metric-label">{{ t('settlement.preview_pending_bets') }}</span>
          </div>
          <div class="preview-metric">
            <span class="preview-metric-value">{{ preview.singleBetCount }} / {{ preview.parlayBetCount }}</span>
            <span class="preview-metric-label">{{ t('settlement.preview_bet_mix') }}</span>
          </div>
          <div class="preview-metric">
            <span class="preview-metric-value preview-metric-green">{{ formatAmount(String(preview.totalPayout ?? 0)) }}</span>
            <span class="preview-metric-label">{{ t('settlement.est_payout') }}</span>
          </div>
          <div class="preview-metric">
            <span class="preview-metric-value preview-metric-orange">{{ formatAmount(String(preview.totalRefund ?? 0)) }}</span>
            <span class="preview-metric-label">{{ t('settlement.refund_amount') }}</span>
          </div>
        </div>
        <el-button type="success" size="small" @click="confirm">
          {{ t('settlement.confirm_btn') }}
        </el-button>
      </div>
      <p v-if="previewZeroHint" class="preview-zero-hint">{{ previewZeroHint }}</p>
      <div v-if="previewItemsPage.total > 0" class="preview-items-wrap">
        <div class="preview-items-head">
          <span class="preview-items-title">
            {{ t('settlement.preview_items_title', { n: previewItemsPage.total }) }}
          </span>
        </div>
        <el-table :data="previewItemsPage.items" size="small" stripe class="preview-items-table">
          <el-table-column type="index" :index="(i: number) => (previewItemsPage.page - 1) * previewItemsPage.pageSize + i + 1" :label="t('common.seq')" width="55" align="center" />
          <el-table-column :label="t('bet.col.bet_no')" prop="betNo" min-width="140" show-overflow-tooltip />
          <el-table-column :label="t('common.type')" width="68">
            <template #default="{ row }">{{ betTypeLabel(row.betType) }}</template>
          </el-table-column>
          <el-table-column :label="t('settlement.preview_col.result')" width="108">
            <template #default="{ row }">{{ previewResultLabel(row.result) }}</template>
          </el-table-column>
          <el-table-column :label="t('settlement.est_payout')" width="92" align="right">
            <template #default="{ row }">{{ formatAmount(row.payout) }}</template>
          </el-table-column>
          <el-table-column :label="t('user.field.remark')" min-width="160" show-overflow-tooltip prop="note" />
        </el-table>
        <el-pagination
          v-if="previewItemsPage.total > 0"
          class="preview-pager bet-pager"
          size="small"
          background
          layout="total, sizes, prev, pager, next"
          :total="previewItemsPage.total"
          :current-page="previewItemsPage.page"
          :page-size="previewItemsPage.pageSize"
          :page-sizes="[10, 20, 50]"
          @current-change="onPreviewPageChange"
          @size-change="onPreviewPageSizeChange"
        />
      </div>
    </el-card>

    <el-card v-loading="statsLoading" class="stats-card" shadow="never">
      <div v-if="stats" class="stats-body">
        <div class="stats-charts">
          <div v-if="betTypeChartOption" class="mini-chart">
            <VChart class="mini-chart-canvas" :option="betTypeChartOption" autoresize />
          </div>
          <div v-if="statusChartOption" class="mini-chart">
            <VChart class="mini-chart-canvas" :option="statusChartOption" autoresize />
          </div>
          <div v-if="selectionStakeChartOption" class="mini-chart">
            <VChart class="mini-chart-canvas" :option="selectionStakeChartOption" autoresize />
          </div>
        </div>

        <div class="stats-tables">
          <div class="stats-table-block">
            <div class="subsection-title">{{ t('settlement.stats_by_market') }}</div>
            <el-table
              v-if="stats.bySelection.length"
              :data="stats.bySelection"
              size="small"
              stripe
              class="stats-table"
              height="100%"
            >
              <el-table-column :label="t('settlement.col.market')" min-width="120">
                <template #default="{ row }">
                  {{ marketLabel(row.marketType) }}
                  <span v-if="row.period" class="period-tag">{{ row.period }}</span>
                </template>
              </el-table-column>
              <el-table-column :label="t('settlement.col.selection')" min-width="120">
                <template #default="{ row }">{{ selectionDisplay(row) }}</template>
              </el-table-column>
              <el-table-column :label="t('settlement.col.legs')" width="72" align="center" prop="legCount" />
              <el-table-column :label="t('settlement.col.single_stake')" width="100" align="right">
                <template #default="{ row }">{{ formatAmount(row.singleStake) }}</template>
              </el-table-column>
              <el-table-column :label="t('settlement.col.parlay_legs')" width="88" align="center" prop="parlayLegCount" />
            </el-table>
            <p v-else class="empty-hint">{{ t('settlement.no_bets') }}</p>
          </div>

          <div class="stats-table-block stats-table-block--bets">
            <div class="subsection-title">
              {{ t('settlement.bet_list') }} ({{ stats.bets.total }})
              <span class="subsection-hint">{{ t('settlement.bet_list_hint') }}</span>
            </div>
            <div v-loading="betsLoading" class="table-wrap">
            <el-table
              v-if="stats.bets.items.length"
              :data="stats.bets.items"
              size="small"
              stripe
              class="stats-table"
            >
              <el-table-column type="index" :index="(i: number) => ((stats?.bets.page ?? 1) - 1) * (stats?.bets.pageSize ?? 10) + i + 1" :label="t('common.seq')" width="55" align="center" />
              <el-table-column prop="betNo" :label="t('bet.col.bet_no')" width="140" />
              <el-table-column prop="username" :label="t('bet.col.player')" width="96" />
              <el-table-column :label="t('common.type')" width="72">
                <template #default="{ row }">
                  {{ betTypeLabel(row.betType) }}
                  <span v-if="row.legCountOnMatch > 1" class="leg-badge">×{{ row.legCountOnMatch }}</span>
                </template>
              </el-table-column>
              <el-table-column :label="t('bet.col.content')" min-width="200">
                <template #default="{ row }">
                  <span class="bet-content-cell">{{ matchBetSelectionSummary(row) }}</span>
                </template>
              </el-table-column>
              <el-table-column :label="t('bet.col.stake')" width="88" align="right">
                <template #default="{ row }">{{ formatAmount(row.stake) }}</template>
              </el-table-column>
              <el-table-column :label="t('common.status')" width="88">
                <template #default="{ row }">
                  <el-tag size="small" :type="betStatusTagType(row.status)">{{ betStatusLabel(row.status) }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column :label="t('bet.col.placed_at')" width="120">
                <template #default="{ row }">{{ formatTime(row.placedAt) }}</template>
              </el-table-column>
            </el-table>
            <p v-else class="empty-hint">{{ t('settlement.no_bets') }}</p>
            </div>
            <el-pagination
              v-if="stats.bets.total > 0"
              class="bet-pager"
              size="small"
              background
              layout="total, sizes, prev, pager, next"
              :total="stats.bets.total"
              :current-page="stats.bets.page"
              :page-size="stats.bets.pageSize"
              :page-sizes="[10, 20, 50]"
              @current-change="onBetPageChange"
              @size-change="onBetPageSizeChange"
            />
          </div>
        </div>
      </div>
    </el-card>
  </div>
</template>

<style scoped>
.settlement-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  min-height: 0;
  width: 100%;
  overflow: hidden;
}

.settle-top-card,
.preview-card {
  flex-shrink: 0;
  border-radius: 12px;
}

.settle-top-card :deep(.el-card__body) {
  padding: 14px 16px;
}

.stats-card {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-radius: 12px;
  overflow: hidden;
}

.stats-card :deep(.el-card__body) {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 10px 12px 12px;
}

.stats-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}

.settle-score-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px 24px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

.outright-settle-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 16px;
}

.outright-settle-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: var(--success-text);
}

.outright-winner-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 240px;
  flex: 1;
}

.outright-winner-select {
  width: min(100%, 360px);
}

.settle-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  flex-shrink: 0;
}

.settle-resettle-reason {
  width: 220px;
}

.match-inline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 16px;
  flex: 1;
  min-width: 280px;
}

.team-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.team-logo-sm {
  width: 36px;
  height: 36px;
  object-fit: contain;
  flex-shrink: 0;
}

.team-name-inline {
  font-size: 16px;
  font-weight: 750;
  color: var(--text);
  white-space: nowrap;
}

.kickoff-inline {
  font-size: 13px;
  color: var(--text-muted);
  white-space: nowrap;
}

.kickoff-inline .meta-k {
  color: #aaa49a;
  margin-right: 4px;
}

.score-inline-group {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 16px 20px;
}

.score-block.compact {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.score-block.compact .score-title {
  margin-bottom: 0;
}

.stat-block {
  padding-left: 2px;
}

.stat-inputs {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.stat-side {
  font-size: 11px;
  color: var(--warning-text);
}

.section-title {
  font-size: 14px;
  font-weight: 600;
  color: #e0e0e0;
  margin-bottom: 10px;
  flex-shrink: 0;
}

.subsection-title {
  font-size: 12px;
  font-weight: 600;
  color: #aaa;
  margin: 0 0 8px;
  flex-shrink: 0;
}

.subsection-hint {
  margin-left: 8px;
  font-size: 10px;
  font-weight: 400;
  color: #666;
}

.leg-badge {
  margin-left: 4px;
  font-size: 10px;
  color: var(--warning-text);
}

.bet-content-cell {
  font-size: 12px;
  color: #bbb;
  line-height: 1.4;
}

.stats-charts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  flex-shrink: 0;
}

.mini-chart {
  border: 1px solid #2a2a2a;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.02);
  padding: 4px 6px 0;
  min-height: 0;
  overflow: hidden;
}

.mini-chart-canvas {
  height: 148px;
  width: 100%;
}

.stats-tables {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: 12px;
  overflow: hidden;
}

.stats-table-block {
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.stats-table-block--bets .table-wrap {
  flex: 1;
  min-height: 0;
  overflow: auto;
}

.bet-pager {
  flex-shrink: 0;
  margin-top: 8px;
  display: flex;
  justify-content: flex-end;
}

.stats-table {
  width: 100%;
}

.period-tag {
  margin-left: 6px;
  font-size: 11px;
  color: #666;
}

.empty-hint {
  margin: 0;
  font-size: 13px;
  color: #666;
  text-align: center;
  padding: 16px 0;
}

.match-league {
  margin: 0 0 10px;
  font-size: 13px;
  color: #888;
}

.vs {
  font-size: 13px;
  font-weight: 700;
  color: var(--warning-text);
  flex-shrink: 0;
}

.score-title {
  font-size: 13px;
  color: #999;
  margin-bottom: 8px;
  font-weight: 500;
}

.score-inputs {
  display: flex;
  align-items: center;
  gap: 10px;
}

.score-sep {
  font-size: 18px;
  color: #666;
  font-weight: 300;
}

.preview-hint {
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.preview-title {
  font-size: 14px;
  font-weight: 600;
  color: #e0e0e0;
  margin-bottom: 12px;
}

.preview-card--compact :deep(.el-card__body) {
  padding: 12px 16px;
}

.preview-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.preview-bar-title {
  font-size: 14px;
  font-weight: 600;
  color: #e0e0e0;
  white-space: nowrap;
}

.preview-metrics {
  display: flex;
  align-items: center;
  gap: 20px;
  flex: 1;
  flex-wrap: wrap;
}

.preview-metric {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.preview-metric-value {
  font-size: 20px;
  font-weight: 700;
  color: #e0e0e0;
}

.preview-metric-green {
  color: var(--green-glow);
}

.preview-metric-orange {
  color: #e8a040;
}

.preview-metric-label {
  font-size: 12px;
  color: #888;
}

.preview-zero-hint {
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: 6px;
  background: rgba(232, 160, 64, 0.12);
  border: 1px solid rgba(232, 160, 64, 0.25);
  color: #e8c080;
  font-size: 12px;
  line-height: 1.5;
}

.preview-items-wrap {
  margin-top: 10px;
}

.preview-items-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}

.preview-items-title {
  font-size: 12px;
  font-weight: 600;
  color: #aaa;
}

.preview-items-table {
  margin-top: 0;
}

.preview-pager {
  margin-top: 8px;
  justify-content: flex-end;
}

.pstat {
  padding: 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid #2a2a2a;
  border-radius: 10px;
  text-align: center;
}

.pstat-value {
  font-size: 22px;
  font-weight: 700;
  color: #e0e0e0;
}

.pstat-green {
  color: var(--green-glow);
  text-shadow: none;
}

.pstat-orange {
  color: #e8a040;
}

.pstat-label {
  font-size: 12px;
  color: #888;
  margin-top: 4px;
}

.confirm-btn {
  margin-top: 16px;
}

@media (max-width: 1100px) {
  .stats-charts {
    grid-template-columns: 1fr;
  }

  .stats-tables {
    grid-template-columns: 1fr;
    overflow-y: auto;
  }

  .stats-table-block {
    min-height: 200px;
  }

}

.section-title,
.preview-title,
.preview-bar-title,
.pstat-value {
  color: var(--text);
}

.subsection-title,
.bet-content-cell,
.match-league,
.score-title,
.preview-metric-label,
.preview-items-title,
.period-tag,
.empty-hint,
.subsection-hint {
  color: var(--text-muted);
}

.stat-side,
.leg-badge,
.vs,
.preview-metric-orange,
.pstat-orange {
  color: var(--warning-text);
}

.preview-metric-green,
.pstat-green {
  color: var(--success-text);
}

.mini-chart,
.pstat {
  border-color: var(--border);
  background: #ffffff;
  box-shadow: var(--shadow);
}

.preview-zero-hint {
  border-color: var(--warning-border);
  background: var(--warning-bg);
  color: var(--warning-text);
}

.score-sep {
  color: var(--text-muted);
}

.stats-table-block--bets .table-wrap {
  border-color: var(--border);
  background: #ffffff;
}

@media (max-width: 760px) {
  .score-row,
  .preview-bar,
  .preview-metrics {
    align-items: stretch;
  }

  .preview-metric {
    justify-content: space-between;
    width: 100%;
  }
}
</style>
