<script setup lang="ts">
import { ref, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import api from '../api';
import { useBetSlipStore } from '../stores/betSlip';
import TeamEmblem from '../components/TeamEmblem.vue';
import { DETAIL_MARKET_TYPES, MARKET_I18N_KEY } from '../utils/marketCatalog';
import MatchBetGuide from '../components/match-detail/MatchBetGuide.vue';
import MarketTypeTile from '../components/match-detail/MarketTypeTile.vue';
import MarketSelectionsPanel from '../components/match-detail/MarketSelectionsPanel.vue';
import CorrectScorePanel from '../components/match-detail/CorrectScorePanel.vue';
import CorrectScoreConfirmModal, {
  type CsConfirmLine,
} from '../components/match-detail/CorrectScoreConfirmModal.vue';
import { isCorrectScoreMarket, parseScoreCode } from '../utils/correctScoreLayout';
import { useOnLocaleChange } from '../composables/useOnLocaleChange';
import { usePullToRefresh } from '../composables/usePullToRefresh';
import vsImg from '../assets/images/vs.png';
import GoldSpinner from '../components/GoldSpinner.vue';
import BetSuccessOverlay from '../components/BetSuccessOverlay.vue';
import cardBg from '../assets/images/card-bg.png';

const heroCardBg = `url(${cardBg})`;

const route = useRoute();
const router = useRouter();
const { t, locale } = useI18n();
const slip = useBetSlipStore();

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

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

interface MatchDetail {
  id: string;
  leagueName?: string;
  leagueLogoUrl?: string | null;
  homeTeamName: string;
  awayTeamName: string;
  homeTeamCode?: string;
  awayTeamCode?: string;
  homeTeamLogoUrl?: string | null;
  awayTeamLogoUrl?: string | null;
  startTime: string;
  stage?: string | null;
  groupName?: string | null;
  markets: Market[];
}

const match = ref<MatchDetail | null>(null);
const loading = ref(true);
const expandedKey = ref<string | null>(null);
const correctScoreStakes = ref<Record<string, number>>({});
const placingCs = ref(false);
const csMessage = ref('');
const showCsSuccess = ref(false);
const csConfirmOpen = ref(false);
const csConfirmMarketType = ref<string | null>(null);
const marketsByType = computed(() => {
  const map = new Map<string, Market>();
  for (const m of match.value?.markets ?? []) {
    map.set(m.marketType, m);
  }
  return map;
});

function marketPromoLabel(marketType: string) {
  const m = marketsByType.value.get(marketType);
  return m?.promoLabel?.trim() || '';
}

const kickoff = computed(() => {
  if (!match.value) return '';
  return new Date(match.value.startTime).toLocaleString(locale.value, {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    hour12: true,
  });
});

function marketLabel(marketType: string) {
  const key = MARKET_I18N_KEY[marketType];
  return key ? t(key) : marketType;
}

function expandKey(marketType: string) {
  return marketType;
}

function isExpanded(marketType: string) {
  return expandedKey.value === expandKey(marketType);
}

function openMarket(marketType: string) {
  if (!marketsByType.value.has(marketType)) return;
  expandedKey.value = expandKey(marketType);
}

function closeMarket() {
  expandedKey.value = null;
}

function toggleMarket(marketType: string) {
  if (!marketsByType.value.has(marketType)) return;
  if (isExpanded(marketType)) closeMarket();
  else openMarket(marketType);
}

function genRequestId() {
  return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
}

const csConfirmLines = computed((): CsConfirmLine[] => {
  const marketType = csConfirmMarketType.value;
  if (!marketType) return [];
  const market = marketsByType.value.get(marketType);
  if (!market) return [];
  return market.selections
    .filter((s) => (correctScoreStakes.value[s.id] ?? 0) > 0)
    .map((s) => {
      const parsed = parseScoreCode(s.selectionCode, t);
      return {
        scoreDisplay: parsed?.display ?? s.selectionName,
        odds: s.odds,
        stake: correctScoreStakes.value[s.id],
      };
    });
});

function openCorrectScoreConfirm(marketType: string) {
  const market = marketsByType.value.get(marketType);
  if (!market || !match.value) return;
  const hasStake = market.selections.some((s) => (correctScoreStakes.value[s.id] ?? 0) > 0);
  if (!hasStake) {
    csMessage.value = t('bet.cs_stake_required');
    return;
  }
  csMessage.value = '';
  csConfirmMarketType.value = marketType;
  csConfirmOpen.value = true;
}

function closeCorrectScoreConfirm() {
  csConfirmOpen.value = false;
}

async function confirmCorrectScoreBets() {
  const marketType = csConfirmMarketType.value;
  if (!marketType) return;
  csConfirmOpen.value = false;
  await placeCorrectScoreBets(marketType);
}

async function placeCorrectScoreBets(marketType: string) {
  const market = marketsByType.value.get(marketType);
  if (!market || !match.value) return;
  const entries = market.selections.filter((s) => (correctScoreStakes.value[s.id] ?? 0) > 0);
  if (!entries.length) {
    csMessage.value = t('bet.cs_stake_required');
    return;
  }
  placingCs.value = true;
  csMessage.value = '';
  try {
    for (const sel of entries) {
      await api.post('/player/bets/single', {
        selectionId: sel.id,
        oddsVersion: String(sel.oddsVersion),
        stake: correctScoreStakes.value[sel.id],
        requestId: genRequestId(),
      });
    }
    csMessage.value = t('bet.cs_place_success');
    const next = { ...correctScoreStakes.value };
    for (const sel of entries) delete next[sel.id];
    correctScoreStakes.value = next;
    showCsSuccess.value = true;
  } catch (e: unknown) {
    csMessage.value =
      (e as { response?: { data?: { error?: string } } })?.response?.data?.error ||
      t('bet.cs_place_failed');
  } finally {
    placingCs.value = false;
  }
}

async function loadMatch() {
  loading.value = true;
  try {
    const { data } = await api.get(`/player/matches/${route.params.id}`);
    match.value = data.data;
  } finally {
    loading.value = false;
  }
}

useOnLocaleChange(loadMatch);

const { pullDistance, spinning, progress } = usePullToRefresh({
  onRefresh: loadMatch,
});

const pullIndicatorStyle = () => ({
  height: `${pullDistance.value}px`,
  opacity: Math.min(pullDistance.value / 48, 1),
});

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

function toggleSelection(sel: Selection, market: Market) {
  if (!match.value) return;
  slip.addItem({
    selectionId: sel.id,
    oddsVersion: String(sel.oddsVersion),
    matchId: match.value.id,
    matchName: `${match.value.homeTeamName} vs ${match.value.awayTeamName}`,
    selectionName: sel.selectionName,
    odds: parseFloat(sel.odds),
    marketType: market.marketType,
    lineValue:
      market.lineValue != null && market.lineValue !== ''
        ? parseFloat(String(market.lineValue))
        : null,
    allowParlay: market.allowParlay,
  });
}

function onPickSelection(selId: string, marketType: string) {
  const market = marketsByType.value.get(marketType);
  const sel = market?.selections.find((s) => s.id === selId);
  if (!market || !sel) return;
  toggleSelection(sel, market);
}

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

/** 当前玩法是否已有选项加入投注单 */
function hasSlipPickForMarket(marketType: string) {
  if (!match.value || slip.mode !== 'single') return false;
  return slip.items.some(
    (item) => item.matchId === match.value!.id && item.marketType === marketType,
  );
}
</script>

<template>
  <div class="detail-page">
    <div
      class="pull-indicator"
      :style="pullIndicatorStyle()"
    >
      <GoldSpinner v-if="spinning" :size="28" :progress="progress" :active="spinning" />
    </div>

    <header class="toolbar">
      <button type="button" class="icon-btn" :aria-label="t('bet.back')" @click="router.back()">←</button>
      <span class="toolbar-title">{{ match?.leagueName ?? '' }}</span>
      <div class="toolbar-actions">
        <MatchBetGuide />
        <button
          type="button"
          class="icon-btn"
          :aria-label="t('bet.refresh')"
          :disabled="loading"
          @click="loadMatch"
        >
          ↻
        </button>
      </div>
    </header>

    <div v-if="loading" class="state">
      <GoldSpinner :size="36" />
    </div>
    <template v-else-if="match">
      <section class="match-hero">
        <div class="hero-teams">
          <!-- home -->
          <div class="hero-team">
            <TeamEmblem
              size="lg"
              :team-code="match.homeTeamCode"
              :team-name="match.homeTeamName"
              :logo-url="match.homeTeamLogoUrl"
            />
            <span class="hero-name">{{ match.homeTeamName }}</span>
          </div>

          <!-- VS arena with lightning -->
          <div class="vs-arena">
            <svg class="hz-lightning" viewBox="0 0 72 28" aria-hidden="true">
              <defs>
                <linearGradient id="hzDetailGrad" x1="0%" y1="0%" x2="100%" y2="0%">
                  <stop offset="0%" stop-color="#5eb8ff" stop-opacity="0.2" />
                  <stop offset="35%" stop-color="#b8ecff" stop-opacity="1" />
                  <stop offset="50%" stop-color="#ffffff" stop-opacity="1" />
                  <stop offset="65%" stop-color="#ffd080" stop-opacity="1" />
                  <stop offset="100%" stop-color="#ff9040" stop-opacity="0.2" />
                </linearGradient>
              </defs>
              <path
                class="hz-path hz-path-main"
                stroke="url(#hzDetailGrad)"
                d="M1 14 H16 L20 5 L24 23 L28 9 L32 14 H40 L44 6 L48 22 L52 12 L56 14 H71"
              />
              <path
                class="hz-path hz-path-sub"
                stroke="url(#hzDetailGrad)"
                d="M3 19 H14 L18 15 L22 19 H50 L54 16 L58 19 H69"
              />
            </svg>
            <span class="hz-beam" aria-hidden="true" />
            <img :src="vsImg" alt="" class="vs-img" />
          </div>

          <!-- away -->
          <div class="hero-team">
            <TeamEmblem
              size="lg"
              :team-code="match.awayTeamCode"
              :team-name="match.awayTeamName"
              :logo-url="match.awayTeamLogoUrl"
            />
            <span class="hero-name">{{ match.awayTeamName }}</span>
          </div>
        </div>

        <p class="kickoff">{{ t('bet.kickoff_time') }}{{ kickoff }}</p>
      </section>

      <section class="markets-section">
        <CorrectScoreConfirmModal
          :open="csConfirmOpen"
          :market-label="csConfirmMarketType ? marketLabel(csConfirmMarketType) : ''"
          :home-team-name="match.homeTeamName"
          :away-team-name="match.awayTeamName"
          :lines="csConfirmLines"
          :loading="placingCs"
          @close="closeCorrectScoreConfirm"
          @confirm="confirmCorrectScoreBets"
        />

        <p v-if="csMessage" class="cs-toast">{{ csMessage }}</p>

        <div class="market-list">
          <div
            v-for="marketType in DETAIL_MARKET_TYPES"
            :key="marketType"
            class="market-group"
            :class="{ open: isExpanded(marketType) }"
          >
            <MarketTypeTile
              :label="marketLabel(marketType)"
              :promo-label="marketPromoLabel(marketType)"
              :has-market="marketsByType.has(marketType)"
              :expanded="isExpanded(marketType)"
              @toggle="toggleMarket(marketType)"
            />
            <template v-if="isExpanded(marketType) && marketsByType.get(marketType)">
              <CorrectScorePanel
                v-if="isCorrectScoreMarket(marketType)"
                :market-type="marketType"
                :selections="marketsByType.get(marketType)!.selections"
                v-model:stakes="correctScoreStakes"
              />
              <button
                v-if="isCorrectScoreMarket(marketType)"
                type="button"
                class="market-foot-btn"
                @click="openCorrectScoreConfirm(marketType)"
              >
                {{ t('bet.cs_confirm_cell') }}
              </button>
              <MarketSelectionsPanel
                v-else
                compact
                :selections="marketsByType.get(marketType)!.selections"
                :is-selected="isSelected"
                @pick="onPickSelection($event, marketType)"
              />
              <button
                v-if="!isCorrectScoreMarket(marketType) && hasSlipPickForMarket(marketType)"
                type="button"
                class="market-foot-btn"
                @click="openBetSlipDrawer"
              >
                {{ t('bet.cs_confirm_cell') }}
              </button>
            </template>
          </div>
        </div>
      </section>
    </template>
  </div>

  <BetSuccessOverlay :show="showCsSuccess" @done="showCsSuccess = false" />
</template>

<style scoped>
.detail-page {
  margin: 0 -16px;
  padding-bottom: 16px;
}

.pull-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  transition: height 0.15s ease;
}

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 12px 8px;
  gap: 8px;
  position: sticky;
  top: -12px;
  z-index: 50;
  margin-top: -12px;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(4px);
}

.toolbar-title {
  flex: 1;
  text-align: center;
  font-size: 13px;
  font-weight: 800;
  color: var(--primary-light);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 4px;
}

.toolbar-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.icon-btn {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--border-gold-soft);
  background: rgba(0, 0, 0, 0.2);
  color: var(--primary-light);
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  padding: 0;
}

.icon-btn:disabled {
  opacity: 0.45;
}

/* ── match hero ── */
.match-hero {
  position: relative;
  isolation: isolate;
  margin: 0 0 10px;
  padding: 14px 12px 16px;
  overflow: hidden;
}

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

.kickoff {
  position: relative;
  z-index: 1;
  font-size: 11px;
  color: var(--text-muted);
  text-align: left;
  margin-top: 10px;
  padding-left: 2px;
}

.hero-teams {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

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

.hero-name {
  font-size: 13px;
  font-weight: 800;
  color: var(--primary-light);
  text-align: center;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* VS arena — same as HomeView */
.vs-arena {
  position: relative;
  flex-shrink: 0;
  width: 140px;
  height: 126px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hz-lightning {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  pointer-events: none;
  overflow: visible;
}

.hz-path {
  fill: none;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 0 4px rgba(120, 210, 255, 0.95)) drop-shadow(0 0 8px rgba(255, 180, 80, 0.55));
  opacity: 0;
}

.hz-path-main {
  animation: hz-strike-main 2.6s ease-in-out infinite;
}

.hz-path-sub {
  stroke-width: 1.6;
  animation: hz-strike-sub 2.6s ease-in-out infinite;
  animation-delay: 0.12s;
}

.hz-beam {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  transform: translateY(-50%);
  pointer-events: none;
  z-index: 1;
  background: linear-gradient(
    90deg,
    rgba(94, 184, 255, 0) 0%,
    rgba(184, 236, 255, 0.95) 28%,
    #fff 50%,
    rgba(255, 208, 128, 0.95) 72%,
    rgba(255, 144, 64, 0) 100%
  );
  opacity: 0;
  filter: blur(0.4px);
  animation: hz-beam-flash 2.6s ease-in-out infinite;
}

.vs-img {
  position: relative;
  z-index: 0;
  width: 126px;
  height: auto;
  object-fit: contain;
}

@keyframes hz-strike-main {
  0%, 72%, 100% { opacity: 0; }
  74% { opacity: 1; }
  75% { opacity: 0.25; }
  76% { opacity: 0.95; }
  78% { opacity: 0; }
}

@keyframes hz-strike-sub {
  0%, 74%, 100% { opacity: 0; }
  76% { opacity: 0.85; }
  77% { opacity: 0.2; }
  78% { opacity: 0.7; }
  80% { opacity: 0; }
}

@keyframes hz-beam-flash {
  0%, 71%, 100% { opacity: 0; transform: translateY(-50%) scaleX(0.6); }
  73% { opacity: 0.85; transform: translateY(-50%) scaleX(1); }
  75% { opacity: 0.15; transform: translateY(-50%) scaleX(0.95); }
  76% { opacity: 0.75; transform: translateY(-50%) scaleX(1); }
  78% { opacity: 0; transform: translateY(-50%) scaleX(1.05); }
}

@keyframes vs-glow {
  0%, 100% {
    opacity: 0.82;
    filter: drop-shadow(0 0 2px rgba(212, 175, 55, 0.3));
  }
  50% {
    opacity: 1;
    filter: drop-shadow(0 0 3px rgba(255, 230, 140, 0.7)) drop-shadow(0 0 6px rgba(212, 175, 55, 0.35));
  }
}

@media (prefers-reduced-motion: reduce) {
  .hz-path, .hz-beam { animation: none; opacity: 0; }
}

.markets-section {
  padding: 0 10px 8px;
}

.market-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 4px 0;
}

.market-group {
  border-radius: 8px;
  border: 1px solid #252525;
  background: linear-gradient(180deg, #1a1a1a 0%, #151515 100%);
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.35);
  transition: border-color 0.2s, box-shadow 0.2s;
}

.market-group.open {
  border-color: rgba(212, 175, 55, 0.25);
  box-shadow: 0 4px 16px rgba(212, 175, 55, 0.08);
}

.market-group :deep(.row) {
  border-radius: 8px;
}

.market-group.open :deep(.row) {
  border-radius: 0;
}

.market-foot-btn {
  display: block;
  width: calc(100% - 16px);
  margin: 0 8px 10px;
  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;
}

.state {
  text-align: center;
  padding: 48px;
  color: var(--text-muted);
}

.cs-toast {
  text-align: center;
  font-size: 12px;
  color: var(--primary-light);
  padding: 2px 0 6px;
}

</style>
