<script setup lang="ts">
import { ref, computed, watch, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n';
import api from '../../api';
import { usePlayerProfile } from '../../composables/usePlayerProfile';
import { formatMoney, parseAmount } from '../../utils/localeDisplay';
import { teamFlagUrl } from '../../utils/teamFlag';
import BetSuccessOverlay from '../BetSuccessOverlay.vue';

export interface OutrightPick {
  selectionId: string;
  oddsVersion: string;
  teamCode: string;
  teamName: string;
  odds: string;
  eventTitle: string;
}

interface SelectionOddsRow {
  id: string;
  odds: string;
  oddsVersion: string;
  status: string;
  marketStatus: string;
  marketShowOnPlayer: boolean;
  matchStatus: string;
}

type OddsDelta = {
  oldOdds: number;
  newOdds: number;
  newVersion: string;
  suspended: boolean;
};

const props = defineProps<{
  open: boolean;
  pick: OutrightPick | null;
}>();

const emit = defineEmits<{ close: [] }>();

const { t, locale } = useI18n();
const { refreshProfile } = usePlayerProfile();

const step = ref<'form' | 'success'>('form');
const stake = ref(1);
const loading = ref(false);
const error = ref('');
const balance = ref(0);
const successBalance = ref(0);
const successStake = ref(0);
const showSuccess = ref(false);
const currentOdds = ref('');
const currentOddsVersion = ref('');
const oddsDelta = ref<OddsDelta | null>(null);
const ODDS_POLL_MS = 5000;
let oddsPollTimer: ReturnType<typeof setInterval> | null = null;

const flagUrl = computed(() =>
  props.pick ? teamFlagUrl(props.pick.teamCode, props.pick.teamName) : null,
);

const balanceText = computed(() => formatMoney(balance.value, locale.value));

const effectiveOdds = computed(() => {
  if (oddsDelta.value && !oddsDelta.value.suspended) return oddsDelta.value.newOdds;
  const n = parseFloat(currentOdds.value);
  return Number.isFinite(n) ? n : 0;
});

const oddsNum = computed(() => effectiveOdds.value);

const estReturn = computed(() => {
  const s = Number(stake.value);
  if (!s || s <= 0 || !oddsNum.value) return 0;
  return s * oddsNum.value;
});

const estReturnText = computed(() => formatMoney(estReturn.value, locale.value));

const hasPendingOddsChanges = computed(() => Boolean(oddsDelta.value && !oddsDelta.value.suspended));
const hasSuspendedSelection = computed(() => Boolean(oddsDelta.value?.suspended));

const oddsWarningText = computed(() => {
  if (hasSuspendedSelection.value) return t('bet.odds_suspended');
  if (hasPendingOddsChanges.value) return t('bet.odds_changed');
  return '';
});

const submitButtonLabel = computed(() => {
  if (loading.value) return t('bet.placing');
  if (hasPendingOddsChanges.value) return t('bet.accept_changes_place');
  return t('bet.place_bet_short');
});

function syncPickState() {
  if (!props.pick) return;
  currentOdds.value = props.pick.odds;
  currentOddsVersion.value = props.pick.oddsVersion;
  oddsDelta.value = null;
}

function acceptPendingOdds() {
  if (!oddsDelta.value || oddsDelta.value.suspended) return;
  currentOdds.value = oddsDelta.value.newOdds.toFixed(2);
  currentOddsVersion.value = oddsDelta.value.newVersion;
  oddsDelta.value = null;
}

function stopOddsPolling() {
  if (oddsPollTimer) {
    clearInterval(oddsPollTimer);
    oddsPollTimer = null;
  }
}

async function pollSelectionOdds() {
  if (!props.pick || !props.open || step.value !== 'form') return;

  try {
    const { data } = await api.get('/player/selections/odds', {
      params: { ids: props.pick.selectionId },
    });
    const row: SelectionOddsRow | undefined = data.data?.items?.[0];
    if (!row) return;

    const suspended =
      row.status !== 'OPEN' ||
      row.marketStatus !== 'OPEN' ||
      row.marketShowOnPlayer === false ||
      row.matchStatus !== 'PUBLISHED';
    const newOdds = parseFloat(row.odds);
    const versionChanged = row.oddsVersion !== currentOddsVersion.value;
    const baseOdds = parseFloat(currentOdds.value);
    const oddsChanged = Number.isFinite(newOdds) && Math.abs(newOdds - baseOdds) > 0.0001;

    if (suspended || versionChanged || oddsChanged) {
      oddsDelta.value = {
        oldOdds: oddsDelta.value?.oldOdds ?? baseOdds,
        newOdds: Number.isFinite(newOdds) ? newOdds : baseOdds,
        newVersion: row.oddsVersion,
        suspended,
      };
    } else {
      oddsDelta.value = null;
    }
  } catch {
    /* silent retry on next tick */
  }
}

function startOddsPolling() {
  stopOddsPolling();
  void pollSelectionOdds();
  oddsPollTimer = setInterval(() => {
    void pollSelectionOdds();
  }, ODDS_POLL_MS);
}

watch(
  () => props.open,
  async (v) => {
    if (!v) {
      stopOddsPolling();
      oddsDelta.value = null;
      return;
    }
    step.value = 'form';
    stake.value = 1;
    error.value = '';
    syncPickState();
    try {
      const { data } = await api.get('/player/profile');
      balance.value = parseAmount(data.data?.wallet?.availableBalance);
    } catch {
      balance.value = 0;
    }
    startOddsPolling();
  },
);

watch(
  () => props.pick?.selectionId,
  () => {
    if (!props.open) return;
    syncPickState();
    if (props.open) void pollSelectionOdds();
  },
);

onUnmounted(() => {
  stopOddsPolling();
});

function close() {
  emit('close');
}

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

function setStake(amount: number) {
  stake.value = Math.max(0.01, Math.round(amount * 100) / 100);
}

function setMaxStake() {
  if (balance.value > 0) setStake(balance.value);
}

async function submit() {
  if (!props.pick || stake.value <= 0) return;
  if (stake.value > balance.value) {
    error.value = t('bet.outright_insufficient');
    return;
  }
  if (hasSuspendedSelection.value) {
    error.value = t('bet.odds_suspended');
    return;
  }
  if (hasPendingOddsChanges.value) {
    acceptPendingOdds();
  }

  loading.value = true;
  error.value = '';
  try {
    await api.post('/player/bets/single', {
      selectionId: props.pick.selectionId,
      oddsVersion: currentOddsVersion.value,
      stake: stake.value,
      requestId: genRequestId(),
    });
    successStake.value = stake.value;
    successBalance.value = balance.value - stake.value;
    balance.value = successBalance.value;
    step.value = 'success';
    showSuccess.value = true;
    void refreshProfile();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string; code?: string } } };
    if (err.response?.data?.code === 'ODDS_CHANGED') {
      await pollSelectionOdds();
      error.value = t('bet.odds_changed');
      return;
    }
    error.value = err.response?.data?.error || t('bet.outright_bet_failed');
  } finally {
    loading.value = false;
  }
}

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

<template>
  <div v-if="open && pick" class="overlay" @click.self="close">
    <div class="modal" role="dialog" aria-modal="true">
      <button type="button" class="close-x" :aria-label="t('bet.cancel')" @click="close">✕</button>

      <template v-if="step === 'form'">
        <p class="hint">{{ t('bet.outright_enter_stake') }}</p>

        <div class="hero">
          <img v-if="flagUrl" :src="flagUrl" alt="" class="flag" />
          <p class="team">{{ pick.teamName }}</p>
          <span
            class="odds-badge"
            :class="{
              'odds-badge--up': oddsDelta && !oddsDelta.suspended && oddsDelta.newOdds >= oddsDelta.oldOdds,
              'odds-badge--down': oddsDelta && !oddsDelta.suspended && oddsDelta.newOdds < oddsDelta.oldOdds,
              'odds-badge--suspended': oddsDelta?.suspended,
            }"
          >
            <template v-if="oddsDelta && !oddsDelta.suspended">
              @ {{ oddsDelta.oldOdds.toFixed(2) }} → {{ oddsDelta.newOdds.toFixed(2) }}
            </template>
            <template v-else>
              @ {{ formatOdds(currentOdds || pick.odds) }}
            </template>
          </span>
        </div>

        <p class="event-title">{{ pick.eventTitle }}</p>

        <p v-if="oddsWarningText" class="odds-warning">{{ oddsWarningText }}</p>

        <div class="balance-row">
          <span class="balance-label">{{ t('bet.outright_balance') }}</span>
          <span class="balance-value">{{ balanceText }}</span>
        </div>

        <label class="stake-label">{{ t('bet.stake_label') }}</label>
        <input
          v-model.number="stake"
          type="number"
          class="stake-input"
          min="0.01"
          step="0.01"
          inputmode="decimal"
          :placeholder="t('bet.stake_placeholder')"
        />

        <div class="quick-stakes">
          <button type="button" class="chip" @click="setStake(50)">50</button>
          <button type="button" class="chip" @click="setStake(100)">100</button>
          <button type="button" class="chip" @click="setStake(500)">500</button>
          <button type="button" class="chip" @click="setMaxStake">{{ t('bet.stake_max') }}</button>
        </div>

        <p class="est-return">
          {{ t('history.est_return') }}：<span class="est-value">{{ estReturnText }}</span>
        </p>

        <p v-if="error" class="error">{{ error }}</p>

        <div class="actions">
          <button type="button" class="btn-cancel" :disabled="loading" @click="close">
            {{ t('bet.cancel') }}
          </button>
          <button
            type="button"
            class="btn-confirm btn-gold-outline"
            :disabled="loading || stake <= 0 || hasSuspendedSelection"
            @click="submit"
          >
            {{ submitButtonLabel }}
          </button>
        </div>
      </template>

      <template v-else>
        <div class="success-icon">✓</div>
        <h3 class="success-title">{{ t('bet.outright_success') }}</h3>

        <div class="hero hero--compact">
          <img v-if="flagUrl" :src="flagUrl" alt="" class="flag" />
          <p class="team">{{ pick.teamName }}</p>
          <span class="odds-badge">@ {{ formatOdds(pick.odds) }}</span>
        </div>

        <p class="event-title">{{ pick.eventTitle }}</p>

        <div class="summary">
          <div class="summary-row">
            <span>{{ t('bet.outright_stake_amount') }}</span>
            <span>{{ formatMoney(successStake, locale) }}</span>
          </div>
          <div class="summary-row">
            <span>{{ t('bet.outright_balance') }}</span>
            <span class="balance-value">{{ formatMoney(successBalance, locale) }}</span>
          </div>
        </div>

        <button type="button" class="btn-done btn-gold-outline" @click="close">
          {{ t('bet.outright_done') }}
        </button>
      </template>
    </div>
  </div>

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

<style scoped>
.overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0, 0, 0, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  backdrop-filter: blur(4px);
}

.modal {
  position: relative;
  width: 100%;
  max-width: 300px;
  background: linear-gradient(165deg, #1a1810 0%, #121212 45%, #0a0a0a 100%);
  border: 1px solid var(--border-gold-soft);
  border-radius: var(--radius);
  padding: 18px 16px 16px;
  text-align: center;
  box-shadow: var(--shadow), 0 0 24px rgba(212, 175, 55, 0.08);
}

.close-x {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1;
}

.hint {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 12px;
  letter-spacing: 0.04em;
}

.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.hero--compact {
  margin-top: 4px;
  margin-bottom: 8px;
}

.flag {
  width: 48px;
  height: 32px;
  object-fit: cover;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}

.team {
  font-size: 20px;
  font-weight: 900;
  color: var(--primary-light);
  letter-spacing: 0.02em;
}

.odds-badge {
  display: inline-block;
  padding: 3px 10px;
  background: rgba(198, 40, 40, 0.85);
  border: 1px solid rgba(255, 120, 120, 0.35);
  color: #fff;
  font-size: 13px;
  font-weight: 800;
  border-radius: 4px;
}

.odds-badge--up {
  background: rgba(22, 101, 52, 0.9);
  border-color: rgba(110, 231, 160, 0.35);
}

.odds-badge--down {
  background: rgba(153, 27, 27, 0.92);
  border-color: rgba(255, 139, 139, 0.4);
}

.odds-badge--suspended {
  background: rgba(120, 83, 14, 0.9);
  border-color: rgba(255, 184, 77, 0.35);
}

.odds-warning {
  margin: 0 0 10px;
  padding: 8px 10px;
  border-radius: 6px;
  background: rgba(244, 162, 97, 0.12);
  border: 1px solid rgba(244, 162, 97, 0.28);
  color: var(--primary-light);
  font-size: 12px;
  line-height: 1.45;
}

.event-title {
  font-size: 11px;
  color: var(--text-muted);
  margin-bottom: 12px;
  line-height: 1.4;
  padding: 0 4px;
}

.balance-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 10px;
  margin-bottom: 12px;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.balance-label {
  font-size: 12px;
  color: var(--text-muted);
  font-weight: 600;
}

.balance-value {
  font-size: 14px;
  font-weight: 800;
  color: var(--primary-light);
}

.stake-label {
  display: block;
  text-align: left;
  font-size: 11px;
  color: var(--text-muted);
  font-weight: 600;
  margin-bottom: 6px;
}

.stake-input {
  width: 100%;
  padding: 11px 12px;
  margin-bottom: 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: #0a0a0a;
  color: var(--text);
  font-size: 18px;
  font-weight: 800;
  text-align: center;
  outline: none;
}

.stake-input:focus {
  border-color: var(--border-gold-soft);
  box-shadow: 0 0 0 1px rgba(212, 175, 55, 0.15);
}

.stake-input::placeholder {
  color: #555;
  font-weight: 500;
  font-size: 14px;
}

.quick-stakes {
  display: flex;
  gap: 6px;
  margin-bottom: 10px;
}

.chip {
  flex: 1;
  padding: 6px 4px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: #141414;
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 700;
}

.chip:active {
  border-color: var(--border-gold-soft);
  color: var(--primary-light);
  background: rgba(212, 175, 55, 0.08);
}

.est-return {
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 12px;
}

.est-value {
  color: var(--primary-light);
  font-weight: 800;
}

.error {
  color: var(--danger);
  font-size: 12px;
  font-weight: 600;
  margin-bottom: 10px;
}

.actions {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 8px;
}

.btn-cancel {
  padding: 10px;
  border-radius: 6px;
  background: #1a1a1a;
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 700;
}

.btn-confirm {
  padding: 10px;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 800;
}

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

.success-icon {
  width: 44px;
  height: 44px;
  margin: 0 auto 8px;
  border-radius: 50%;
  border: 2px solid rgba(46, 158, 94, 0.8);
  background: rgba(46, 158, 94, 0.12);
  color: #4ade80;
  font-size: 24px;
  font-weight: 900;
  line-height: 40px;
}

.success-title {
  font-size: 16px;
  font-weight: 800;
  color: var(--primary-light);
  margin-bottom: 10px;
}

.summary {
  margin: 12px 0 14px;
  padding: 10px;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.summary-row {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--text-muted);
  padding: 4px 0;
}

.summary-row + .summary-row {
  margin-top: 4px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

.btn-done {
  width: 100%;
  padding: 11px;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 800;
}
</style>
