<script setup lang="ts">
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useBetSlipStore } from '../../stores/betSlip';
import { usePlayerMatches, type ParlayMatch } from '../../composables/usePlayerMatches';
import { useAuthStore } from '../../stores/auth';
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 cardBg from '../../assets/images/card-bg.webp';
import { matchPhaseLabel, type MatchPhase } from '../../utils/matchPhase';

const matchCardBg = `url(${cardBg})`;
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[];
}

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

function goLogin() {
  auth.showLoginPrompt('/bet');
}

const { parlayMatches, parlayLoading, loadParlay } = usePlayerMatches();
const matches = parlayMatches;
const loading = parlayLoading;
const timeFilter = ref<TimeFilter>('all');
const leagueFilter = ref('');
const showClosed = ref(false);
const collapsed = ref<Set<string>>(new Set());

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

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

useOnLocaleChange(async () => {
  await loadParlay(true);
  syncCollapsedAfterLoad();
});

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);
  }
  if (!showClosed.value) {
    list = list.filter((m) => {
      const phase = m.matchPhase ?? (m.bettingOpen === false ? 'closed_pending' : 'open');
      return phase === 'open' || phase === undefined;
    });
  }
  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) {
  if (match.bettingOpen === false) return;
  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 if (err === 'SAME_MATCH') parlayHint.value = t('bet.parlay_same_match');
  else parlayHint.value = '';
}

function openSlip() {
  if (!auth.token) {
    goLogin();
    return;
  }
  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>
      <button
        type="button"
        class="phase-toggle"
        :class="{ 'phase-toggle--active': showClosed }"
        @click="showClosed = !showClosed"
      >
        {{ showClosed ? t('bet.show_all_matches') : t('bet.show_open_only') }}
      </button>
      <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),
          'match-card--phase': (match.matchPhase ?? (match.bettingOpen === false ? 'closed_pending' : 'open')) !== 'open',
        }"
      >
        <button type="button" class="match-head" @click="toggleCollapse(match.id)">
          <span
            v-if="(match.matchPhase ?? (match.bettingOpen === false ? 'closed_pending' : 'open')) === 'settled'"
            class="status-tag status-tag--settled"
          >{{ matchPhaseLabel(t, 'settled') }}</span>
          <span
            v-else-if="(match.matchPhase ?? (match.bettingOpen === false ? 'closed_pending' : 'open')) === 'closed_pending'"
            class="status-tag status-tag--pending"
          >{{ matchPhaseLabel(t, 'closed_pending') }}</span>

          <div class="match-head-top">
            <span class="toggle-icon" :class="{ open: !collapsed.has(match.id) }" aria-hidden="true">
              <span class="toggle-mark">{{ collapsed.has(match.id) ? '+' : '−' }}</span>
            </span>
            <span class="m-league">{{ match.leagueName }}</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
                v-if="(match.matchPhase ?? (match.bettingOpen === false ? 'closed_pending' : 'open')) !== 'open' && match.score"
                class="m-score"
              >
                {{ match.score.ftHome }} - {{ match.score.ftAway }}
              </span>
              <span v-else class="m-time">{{ formatKickoff(match.startTime) }}</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),
                      'odd-btn--locked': match.bettingOpen === false,
                    }"
                    :disabled="match.bettingOpen === false"
                    @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;
}

.phase-toggle {
  padding: 7px 10px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  color: #888;
  background: #141414;
  border: 1px solid #333;
  cursor: pointer;
  transition: all 0.2s ease;
  white-space: nowrap;
}

.phase-toggle--active {
  color: var(--primary-light);
  border-color: var(--primary);
  background: rgba(200, 168, 78, 0.08);
}

.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 {
  position: relative;
  background: linear-gradient(180deg, #1a1a1a 0%, #0d0d0d 100%);
  border: 1px solid rgba(140, 140, 140, 0.35);
  border-radius: 8px;
  overflow: hidden;
}

.match-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(100, 100, 100, 0.08) 0%, transparent 50%, rgba(80, 80, 80, 0.05) 100%);
  pointer-events: none;
  z-index: 0;
}

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

.match-head {
  position: relative;
  z-index: 1;
  overflow: visible;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  padding: 10px 12px 12px;
  background: none;
  border: none;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
}

.match-head::before {
  content: '';
  position: absolute;
  inset: 0;
  background: v-bind(matchCardBg) center / 100% 100% no-repeat;
  opacity: 0.25;
  z-index: -1;
  pointer-events: none;
}

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

.match-head-top {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

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

.toggle-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 0;
}

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

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

.m-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  min-width: 50px;
  padding: 0 2px;
}

.m-name {
  font-size: 13px;
  font-weight: 800;
  color: #fff;
  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: 11px;
  font-weight: 600;
  color: var(--text-muted);
}

.toggle-icon {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #141414;
  border: 1px solid var(--border-gold-soft);
  display: flex;
  align-items: center;
  justify-content: center;
}

.toggle-icon.open {
  border-color: var(--border-gold-soft);
}

.toggle-mark {
  color: var(--primary-light);
  font-size: 17px;
  font-weight: 900;
  line-height: 1;
  margin-top: -1px;
}

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

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

.market-blocks {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 10px 10px;
  border-top: 1px solid rgba(140, 140, 140, 0.15);
}

.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-btn--locked {
  opacity: 0.65;
  cursor: not-allowed;
  border-color: #333;
}

.odd-btn--locked .odd-label,
.odd-btn--locked .odd-val {
  color: #666;
}

.match-card--phase {
  opacity: 0.94;
}

.status-tag {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 5;
  font-size: 10px;
  font-weight: 800;
  padding: 3px 10px;
  border-radius: 0 6px 0 8px;
  line-height: 1.3;
  white-space: nowrap;
  letter-spacing: 0.02em;
}

.status-tag--settled {
  background: linear-gradient(180deg, #2a2a3a 0%, #1a1a28 100%);
  color: #8a9ab8;
  border-bottom: 1px solid rgba(120, 140, 180, 0.3);
  border-left: 1px solid rgba(120, 140, 180, 0.3);
}

.status-tag--pending {
  background: linear-gradient(180deg, #3a3a2a 0%, #28251a 100%);
  color: #c8a84e;
  border-bottom: 1px solid rgba(200, 168, 78, 0.3);
  border-left: 1px solid rgba(200, 168, 78, 0.3);
}

.m-score {
  font-size: 15px;
  font-weight: 900;
  color: #fff;
  letter-spacing: 0.04em;
}

.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>
