<script setup lang="ts">
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import api from '../../api';
import { useBetSlipStore } from '../../stores/betSlip';
import { PARLAY_MAX_LEGS, canSelectForParlay } from '@thebet365/shared';
import { PARLAY_MARKET_TYPES, PARLAY_SELECTION_KEYS, PARLAY_MARKET_GROUPS } from '../../utils/parlayColumns';
import BetGuideHelp from '../BetGuideHelp.vue';
import GoldSpinner from '../GoldSpinner.vue';
import TeamEmblem from '../TeamEmblem.vue';
import { useOnLocaleChange } from '../../composables/useOnLocaleChange';

type TimeFilter = 'all' | 'today';

interface Selection {
  id: string;
  selectionCode: string;
  selectionName: string;
  odds: string;
  oddsVersion: string;
}

interface Market {
  id: string;
  marketType: string;
  lineValue?: string | number | null;
  allowParlay?: boolean;
  selections: Selection[];
}

interface ParlayMatch {
  id: string;
  leagueName: string;
  leagueId?: string;
  homeTeamName: string;
  awayTeamName: string;
  homeTeamCode?: string;
  awayTeamCode?: string;
  homeTeamLogoUrl?: string | null;
  awayTeamLogoUrl?: string | null;
  startTime: string;
  markets: Market[];
}

const { t, locale } = useI18n();
const slip = useBetSlipStore();

const loading = ref(true);
const matches = ref<ParlayMatch[]>([]);
const timeFilter = ref<TimeFilter>('all');
const leagueFilter = ref('');
const collapsed = ref<Set<string>>(new Set());

const parlayMarketKeys = PARLAY_MARKET_TYPES.map((c) => c.key);

async function loadParlayMatches() {
  const hadData = matches.value.length > 0;
  if (!hadData) loading.value = true;
  try {
    const { data } = await api.get('/player/matches');
    const fresh = (data.data ?? []).filter(
      (m: ParlayMatch) => m.markets?.length && hasParlayMarkets(m),
    );
    if (!hadData) {
      matches.value = fresh;
      syncCollapsedAfterLoad();
    } else {
      mergeOddsOnly(fresh);
    }
  } finally {
    if (!hadData) loading.value = false;
  }
}

function syncCollapsedAfterLoad() {
  const ids = matches.value.map((m) => m.id);
  // 只保留仍然存在的 id
  const kept = [...collapsed.value].filter((id) => ids.includes(id));
  if (kept.length > 0) {
    collapsed.value = new Set(kept);
    return;
  }
  // 默认只展开第一个，其余折叠
  if (ids.length > 1) {
    collapsed.value = new Set(ids.slice(1));
  } else {
    collapsed.value = new Set();
  }
}

function mergeOddsOnly(fresh: ParlayMatch[]) {
  const matchMap = new Map<string, ParlayMatch>();
  for (const m of fresh) matchMap.set(m.id, m);

  for (const match of matches.value) {
    const freshMatch = matchMap.get(match.id);
    if (!freshMatch) continue;
    const marketMap = new Map<string, Market>();
    for (const mk of freshMatch.markets) marketMap.set(mk.id, mk);
    for (const market of match.markets) {
      const freshMarket = marketMap.get(market.id);
      if (!freshMarket) continue;
      const selMap = new Map<string, Selection>();
      for (const s of freshMarket.selections) selMap.set(s.id, s);
      for (const sel of market.selections) {
        const fs = selMap.get(sel.id);
        if (fs) {
          sel.odds = fs.odds;
          sel.oddsVersion = fs.oddsVersion;
        }
      }
    }
  }
}

useOnLocaleChange(loadParlayMatches);

const leagues = computed(() => {
  const seen = new Set<string>();
  const list: { id: string; name: string }[] = [];
  for (const m of matches.value) {
    const id = m.leagueId ?? m.leagueName;
    if (!seen.has(id)) {
      seen.add(id);
      list.push({ id, name: m.leagueName });
    }
  }
  list.sort((a, b) => a.name.localeCompare(b.name));
  return list;
});

function parseLine(v: string | number | null | undefined) {
  if (v == null || v === '') return null;
  const n = typeof v === 'number' ? v : parseFloat(String(v));
  return Number.isFinite(n) ? n : null;
}

function isParlayEligibleMarket(market: Market) {
  if (!market.selections.length) return false;
  return canSelectForParlay({
    marketType: market.marketType,
    lineValue: parseLine(market.lineValue),
    allowParlay: market.allowParlay ?? true,
  }).ok;
}

function hasParlayMarkets(m: ParlayMatch) {
  return parlayMarketKeys.some((key) => {
    const market = m.markets?.find((mk) => mk.marketType === key);
    return market && isParlayEligibleMarket(market);
  });
}

function getMarket(m: ParlayMatch, marketType: string) {
  return m.markets?.find((mk) => mk.marketType === marketType);
}

function dayStart(d: Date) {
  const x = new Date(d);
  x.setHours(0, 0, 0, 0);
  return x;
}

function isKickoffToday(startTime: string) {
  const kick = new Date(startTime);
  const now = new Date();
  const start = dayStart(now);
  const end = new Date(start);
  end.setDate(end.getDate() + 1);
  return kick >= start && kick < end;
}

const filteredMatches = computed(() => {
  let list = matches.value;
  if (timeFilter.value === 'today') {
    list = list.filter((m) => isKickoffToday(m.startTime));
  }
  if (leagueFilter.value) {
    list = list.filter((m) => (m.leagueId ?? m.leagueName) === leagueFilter.value);
  }
  return list;
});

function formatKickoff(startTime: string) {
  return new Date(startTime).toLocaleString(locale.value, {
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  });
}

function formatOdds(odds: string) {
  const n = parseFloat(odds);
  return Number.isFinite(n) ? n.toFixed(2) : odds;
}

function selLabel(sel: Selection) {
  const key = PARLAY_SELECTION_KEYS[sel.selectionCode];
  if (key) return t(`bet.${key}`);
  return sel.selectionName.slice(0, 2);
}

function colLabel(labelKey: string) {
  return t(`bet.${labelKey}`);
}

function isPicked(selectionId: string) {
  return slip.items.some((i) => i.selectionId === selectionId);
}

const parlayHint = ref('');

function pickSelection(match: ParlayMatch, market: Market, sel: Selection) {
  const err = slip.addParlayLeg({
    selectionId: sel.id,
    oddsVersion: String(sel.oddsVersion),
    matchId: match.id,
    matchName: `${match.homeTeamName} vs ${match.awayTeamName}`,
    selectionName: `${selLabel(sel)} ${formatOdds(sel.odds)}`,
    odds: parseFloat(sel.odds),
    marketType: market.marketType,
    lineValue: parseLine(market.lineValue),
    allowParlay: market.allowParlay,
  });
  if (err === 'MAX_LEGS') parlayHint.value = t('bet.parlay_max_legs');
  else if (err === 'QUARTER_LINE') parlayHint.value = t('bet.parlay_block_quarter');
  else if (err === 'OUTRIGHT') parlayHint.value = t('bet.parlay_block_outright');
  else if (err === 'NOT_ALLOWED') parlayHint.value = t('bet.parlay_block_not_allowed');
  else parlayHint.value = '';
}

function openSlip() {
  slip.openDrawer();
}

const showParlayFoot = computed(() => slip.mode === 'parlay' && slip.count > 0);

const footConfirmLabel = computed(() =>
  slip.canPlaceParlay ? t('bet.parlay_confirm_parlay') : t('bet.cs_confirm_cell'),
);

function toggleCollapse(id: string) {
  const next = new Set(collapsed.value);
  if (next.has(id)) next.delete(id);
  else next.add(id);
  collapsed.value = next;
}
</script>

<template>
  <div class="parlay-panel" :class="{ 'has-fixed-foot': showParlayFoot }">
    <div class="toolbar">
      <select v-model="timeFilter" class="filter-select">
        <option value="all">{{ t('bet.parlay_filter_all') }}</option>
        <option value="today">{{ t('bet.tab_today') }}</option>
      </select>
      <select v-model="leagueFilter" class="filter-select">
        <option value="">{{ t('bet.parlay_filter_all') }}</option>
        <option v-for="lg in leagues" :key="lg.id" :value="lg.id">{{ lg.name }}</option>
      </select>
      <BetGuideHelp
        :title="t('bet.parlay_guide_title')"
        :aria-label="t('bet.parlay_guide_help')"
        storage-key="thebet365_parlay_guide_seen"
      >
        <p class="intro">{{ t('bet.parlay_desc') }}</p>
        <ol>
          <li>{{ t('bet.parlay_guide_1') }}</li>
          <li>{{ t('bet.parlay_guide_2') }}</li>
          <li>{{ t('bet.parlay_guide_3') }}</li>
        </ol>
        <p class="rules-link">{{ t('bet.guide_rules_link') }}</p>
      </BetGuideHelp>
    </div>

    <div v-if="loading" class="state">
      <GoldSpinner :size="36" />
    </div>

    <div v-else-if="filteredMatches.length" class="match-list">
      <div v-for="match in filteredMatches" :key="match.id" class="match-card" :class="{ collapsed: collapsed.has(match.id) }">
        <button type="button" class="match-head" @click="toggleCollapse(match.id)">
          <div class="match-head-top">
            <span class="m-league">{{ match.leagueName }}</span>
            <span class="toggle-dot" :class="{ open: !collapsed.has(match.id) }">
              {{ collapsed.has(match.id) ? '+' : '−' }}
            </span>
          </div>
          <div class="match-head-teams">
            <div class="m-team home">
              <TeamEmblem
                size="sm"
                :team-code="match.homeTeamCode"
                :team-name="match.homeTeamName"
                :logo-url="match.homeTeamLogoUrl"
              />
              <span class="m-name">{{ match.homeTeamName }}</span>
            </div>
            <div class="m-center">
              <span class="m-time">{{ formatKickoff(match.startTime) }}</span>
              <span class="m-vs">VS</span>
            </div>
            <div class="m-team away">
              <span class="m-name">{{ match.awayTeamName }}</span>
              <TeamEmblem
                size="sm"
                :team-code="match.awayTeamCode"
                :team-name="match.awayTeamName"
                :logo-url="match.awayTeamLogoUrl"
              />
            </div>
          </div>
        </button>

        <div v-show="!collapsed.has(match.id)" class="market-blocks">
          <div v-for="group in PARLAY_MARKET_GROUPS" :key="group.headerKey" class="market-group">
            <div class="group-header">{{ colLabel(group.headerKey) }}</div>
            <div
              v-for="col in group.columns"
              :key="col.key"
              class="market-row"
            >
              <span class="block-label">{{ colLabel(col.labelKey) }}</span>
              <div class="block-btns">
                <template
                  v-if="
                    getMarket(match, col.key) &&
                    isParlayEligibleMarket(getMarket(match, col.key)!)
                  "
                >
                  <button
                    v-for="sel in getMarket(match, col.key)!.selections"
                    :key="sel.id"
                    type="button"
                    class="odd-btn"
                    :class="{ picked: isPicked(sel.id) }"
                    @click="pickSelection(match, getMarket(match, col.key)!, sel)"
                  >
                    <span class="odd-label">{{ selLabel(sel) }}</span>
                    <span class="odd-val">{{ formatOdds(sel.odds) }}</span>
                  </button>
                </template>
                <span v-else class="market-empty">—</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div v-else class="empty">
      <span class="empty-icon" aria-hidden="true">&#9917;</span>
      <p>{{ t('bet.parlay_empty') }}</p>
    </div>

    <Teleport to="body">
      <div v-if="showParlayFoot" class="parlay-foot-fixed">
        <p v-if="parlayHint" class="foot-hint foot-hint--warn">{{ parlayHint }}</p>
        <p v-else-if="slip.count < 2" class="foot-hint">{{ t('bet.parlay_need_more') }}</p>
        <p v-else-if="slip.count > PARLAY_MAX_LEGS" class="foot-hint">{{ t('bet.parlay_max_legs') }}</p>
        <p v-else class="foot-meta">
          {{ t('bet.bet_slip') }} ({{ slip.count }}) · {{ t('bet.parlay') }}
          {{ slip.totalOdds.toFixed(2) }}
        </p>
        <button
          type="button"
          class="market-foot-btn"
          :disabled="!slip.canPlaceParlay"
          @click="openSlip"
        >
          {{ footConfirmLabel }}
        </button>
      </div>
    </Teleport>
  </div>
</template>

<style scoped>
.parlay-panel {
  padding: 0 12px 16px;
}

.parlay-panel.has-fixed-foot {
  padding-bottom: 100px;
}

.toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}

.filter-select {
  padding: 7px 10px;
  border-radius: 6px;
  background: #141414;
  border: 1px solid var(--border-gold-soft);
  color: var(--primary-light);
  font-size: 12px;
  font-weight: 700;
  max-width: 140px;
}

.parlay-foot-fixed {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(50px + env(safe-area-inset-bottom, 0px));
  z-index: 95;
  padding: 10px 12px 10px;
  background: rgba(14, 14, 14, 0.98);
  border-top: 1px solid var(--border-gold-soft);
  box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.45);
}

.foot-hint,
.foot-meta {
  margin: 0 0 8px;
  font-size: 11px;
}

.foot-hint--warn {
  color: var(--danger);
  text-align: center;
}

.foot-meta {
  color: var(--primary-light);
  font-weight: 600;
}

.market-foot-btn {
  display: block;
  width: 100%;
  padding: 9px;
  border-radius: 4px;
  border: 1px solid var(--border-gold-soft);
  background: rgba(212, 175, 55, 0.1);
  color: var(--primary-light);
  font-size: 13px;
  font-weight: 800;
}

.market-foot-btn:disabled {
  opacity: 0.45;
}

.match-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.match-card {
  background: #141414;
  border: 1px solid #2a2a2a;
  border-radius: 8px;
  overflow: hidden;
}

.match-card.collapsed {
  border-color: #222;
}

.match-head {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  padding: 8px 10px;
  background: none;
  border: none;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
}

.match-head:active {
  background: rgba(255, 255, 255, 0.015);
}

.match-head-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-width: 0;
}

.match-head-teams {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 0;
}

.m-team {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
  min-width: 0;
}

.m-team.away {
  justify-content: flex-end;
}

.m-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  flex-shrink: 0;
  padding: 0 4px;
}

.m-name {
  font-size: 12px;
  font-weight: 800;
  color: var(--primary-light);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.m-vs {
  font-size: 9px;
  font-weight: 700;
  color: #555;
  letter-spacing: 0.05em;
}

.m-time {
  font-size: 10px;
  color: var(--text-muted);
}

.toggle-dot {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #1a1a1a;
  border: 1px solid #333;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 900;
  color: #666;
  line-height: 1;
  transition: all 0.15s;
}

.toggle-dot.open {
  border-color: var(--border-gold-soft);
  color: var(--primary-light);
}

.m-league {
  font-size: 10px;
  color: var(--text-muted);
  font-weight: 600;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.match-head :deep(.team-emblem) {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.market-blocks {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 10px 10px;
  border-top: 1px solid #1e1e1e;
}

.market-group {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px 6px;
}

.group-header {
  grid-column: 1 / -1;
  font-size: 10px;
  font-weight: 700;
  color: #888;
  letter-spacing: 0.04em;
  padding: 0 2px 2px;
  border-bottom: 1px solid #1e1e1e;
}

.market-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.block-label {
  font-size: 9.5px;
  font-weight: 800;
  color: var(--text-muted);
  letter-spacing: 0.03em;
  padding: 0 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.block-btns {
  display: flex;
  gap: 3px;
  flex-wrap: wrap;
}

.odd-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 5px 8px;
  border-radius: 4px;
  background: #0d0d0d;
  border: 1px solid #333;
  min-width: 44px;
  min-height: 36px;
}

.odd-btn.picked {
  border-color: var(--primary);
  background: rgba(212, 175, 55, 0.15);
}

.odd-label {
  font-size: 8.5px;
  font-weight: 700;
  color: var(--text-muted);
  line-height: 1;
}

.odd-val {
  font-size: 11px;
  font-weight: 800;
  color: var(--primary-light);
  line-height: 1.1;
}

.market-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  color: #444;
  font-size: 12px;
}

.state,
.empty {
  text-align: center;
  padding: 48px 16px;
  color: var(--text-muted);
  font-weight: 600;
}

.empty-icon {
  display: block;
  font-size: 40px;
  margin-bottom: 12px;
  opacity: 0.45;
}

.empty p {
  font-size: 13px;
}
</style>
