<script setup lang="ts">
import { computed, ref, watch, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useDesktopBetPopover } from '../../composables/useDesktopBetPopover';
import { useBetSlipStore, type ParlaySlipError } from '../../stores/betSlip';
import { useAuthStore } from '../../stores/auth';
import { formatMoney } from '../../utils/localeDisplay';
import api from '../../api';
import { usePlayerProfile } from '../../composables/usePlayerProfile';
import ConfirmDialog from '../ConfirmDialog.vue';
import { buildBetPlaceConfirmMessage } from '../../utils/betPlaceConfirmMessage';
import { useAppToast } from '../../composables/useAppToast';

const { t, locale } = useI18n();
const { visible, anchorX, anchorY, pendingItem, placement, close, cancelClose, scheduleClose } = useDesktopBetPopover();
const slip = useBetSlipStore();
const auth = useAuthStore();
const { refreshProfile } = usePlayerProfile();
const { showToast } = useAppToast();

const popRef = ref<HTMLElement | null>(null);
const stakeInput = ref('5');
const loading = ref(false);
const error = ref('');
const showPlaceConfirm = ref(false);
const placeConfirmMessage = ref('');
const MIN_STAKE = 5;
const confirmingItem = ref<typeof pendingItem.value>(null);

watch(showPlaceConfirm, (val) => {
  if (!val) confirmingItem.value = null;
});

function onPopoverMouseLeave() {
  if (showPlaceConfirm.value) return;
  scheduleClose(200);
}

let outsideClickTimer = 0;

const stake = computed(() => {
  const n = Number.parseFloat(stakeInput.value);
  return Number.isFinite(n) ? n : 0;
});

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

watch(visible, (open) => {
  if (open) {
    error.value = '';
    stakeInput.value = String(Math.max(MIN_STAKE, slip.stake || MIN_STAKE));
    window.clearTimeout(outsideClickTimer);
    outsideClickTimer = window.setTimeout(() => {
      document.addEventListener('click', onDocumentClick, true);
    }, 0);
  } else {
    document.removeEventListener('click', onDocumentClick, true);
  }
});

onUnmounted(() => {
  window.clearTimeout(outsideClickTimer);
  document.removeEventListener('click', onDocumentClick, true);
});

function onDocumentClick(event: MouseEvent) {
  if (!visible.value || !popRef.value || showPlaceConfirm.value) return;
  const target = event.target as Node;
  if (target instanceof Element && target.closest('.confirm-overlay')) return;
  if (!popRef.value.contains(target)) {
    close();
  }
}

function parlayErrorMessage(reason: ParlaySlipError) {
  if (reason === 'MAX_LEGS') return t('bet.parlay_max_legs');
  if (reason === 'QUARTER_LINE') return t('bet.parlay_block_quarter');
  if (reason === 'OUTRIGHT') return t('bet.parlay_block_outright');
  if (reason === 'NOT_ALLOWED') return t('bet.parlay_block_not_allowed');
  if (reason === 'SAME_MATCH') return t('bet.slip_parlay_same_match');
  return t('bet.parlay_block_not_allowed');
}

function validatePlaceNow(): boolean {
  const item = pendingItem.value;
  if (!item) return false;
  if (!auth.token) {
    auth.showLoginPrompt();
    return false;
  }
  if (item.allowSingle === false) {
    error.value = t('bet.slip_parlay_only_hint');
    return false;
  }
  if (stake.value < MIN_STAKE) {
    error.value = t('bet.slip_min_error', { amount: MIN_STAKE });
    return false;
  }
  return true;
}

function onPlaceNowClick() {
  if (!validatePlaceNow()) return;
  const item = pendingItem.value!;
  confirmingItem.value = item;
  placeConfirmMessage.value = buildBetPlaceConfirmMessage(t, {
    mode: 'single',
    items: [item],
    totalStake: stake.value,
    totalReturn: estReturn.value,
    formatMoney: (amount) => formatMoney(amount, locale.value),
    getStake: () => stake.value,
    getOdds: (row) => row.odds,
  });
  showPlaceConfirm.value = true;
}

async function executePlaceNow(item = confirmingItem.value || pendingItem.value) {
  if (!item) return;
  loading.value = true;
  error.value = '';
  try {
    await api.post('/player/bets/single', {
      selectionId: item.selectionId,
      oddsVersion: item.oddsVersion,
      stake: stake.value,
      requestId: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
    });
    slip.stake = stake.value;
    await refreshProfile();
    showPlaceConfirm.value = false;
    close();
    showToast(t('bet.place_success'));
  } catch (e: unknown) {
    error.value =
      (e as { response?: { data?: { error?: string } } })?.response?.data?.error ||
      t('bet.place_failed');
    showToast(error.value);
  } finally {
    loading.value = false;
  }
}

async function confirmPlaceNow() {
  const item = confirmingItem.value;
  if (!item) return;
  showPlaceConfirm.value = false;
  await executePlaceNow(item);
}

function addToSingleList() {
  const item = pendingItem.value;
  if (!item) return;
  if (!auth.token) {
    auth.showLoginPrompt();
    return;
  }
  if (item.allowSingle === false) {
    error.value = t('bet.slip_parlay_only_hint');
    return;
  }
  slip.stake = Math.max(MIN_STAKE, stake.value || MIN_STAKE);
  const before = slip.singleCartItems.length;
  slip.addToSingleCart(item);
  close();
  showToast(t(before < slip.singleCartItems.length ? 'bet.added_to_single' : 'bet.pick_added'));
}

function addToParlayList() {
  const item = pendingItem.value;
  if (!item) return;
  if (!auth.token) {
    auth.showLoginPrompt();
    return;
  }
  slip.stake = Math.max(MIN_STAKE, stake.value || MIN_STAKE);
  const before = slip.parlayItems.length;
  const err = slip.addParlayLeg(item);
  if (err) {
    error.value = parlayErrorMessage(err);
    showToast(error.value);
    return;
  }
  close();
  showToast(t(before < slip.parlayItems.length ? 'bet.added_to_parlay' : 'bet.pick_added'));
}
</script>

<template>
  <Teleport to="body">
    <div
      v-if="visible && pendingItem"
      ref="popRef"
      class="bet-popover"
      :class="`placement-${placement}`"
      tabindex="-1"
      :style="{ left: `${anchorX}px`, top: `${anchorY}px` }"
      @click.stop
      @mouseenter="cancelClose"
      @mouseleave="onPopoverMouseLeave"
    >

      <div class="pop-match">{{ pendingItem.matchName }}</div>
        <div class="pop-market">{{ pendingItem.marketName }}</div>
        <div class="pop-pick-row">
          <span class="pick">{{ pendingItem.selectionName }}</span>
          <span class="odds">@ {{ pendingItem.odds.toFixed(2) }}</span>
        </div>

        <label class="stake-label">{{ t('bet.stake') }}</label>
        <input v-model="stakeInput" type="number" class="stake-input" min="5" step="1" />

        <div class="est-row">
          <span>{{ t('bet.slip_est_return') }}</span>
          <span class="est-val">{{ formatMoney(estReturn, locale) }}</span>
        </div>

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

        <div class="pop-actions">
          <button type="button" class="btn-primary-gold" :disabled="loading" @click="onPlaceNowClick">
            {{ loading ? t('bet.placing') : t('bet.place_now') }}
          </button>
          <div class="pop-actions-row">
            <button type="button" class="btn-outline" @click="addToSingleList">
              {{ t('bet.add_to_single') }}
            </button>
            <button type="button" class="btn-outline" @click="addToParlayList">
              {{ t('bet.add_to_parlay') }}
            </button>
          </div>
        </div>
    </div>
  </Teleport>

  <ConfirmDialog
    v-model:visible="showPlaceConfirm"
    :title="t('bet.place_confirm_title')"
    :message="placeConfirmMessage"
    :confirm-text="t('bet.place_bet')"
    :cancel-text="t('common.cancel')"
    :loading="loading"
    @confirm="confirmPlaceNow"
  />

</template>

<style scoped>
.bet-popover {
  position: fixed;
  z-index: 1000;
  width: 172px;
  padding: 6px 7px;
  border-radius: 6px;
  border: 1px solid var(--border-active);
  background: var(--bg-card);
  box-shadow: 0 6px 18px rgba(0, 61, 107, 0.14);
}

.bet-popover::after,
.bet-popover::before {
  content: '';
  position: absolute;
  width: 0;
  height: 0;
  border-style: solid;
  pointer-events: none;
}

.bet-popover.placement-bottom::after {
  top: -6px;
  left: 50%;
  transform: translateX(-50%);
  border-width: 0 6px 6px 6px;
  border-color: transparent transparent var(--bg-card) transparent;
  z-index: 2;
}

.bet-popover.placement-bottom::before {
  top: -7px;
  left: 50%;
  transform: translateX(-50%);
  border-width: 0 7px 7px 7px;
  border-color: transparent transparent var(--border-active) transparent;
  z-index: 1;
}

.bet-popover.placement-top::after {
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  border-width: 6px 6px 0 6px;
  border-color: var(--bg-card) transparent transparent transparent;
  z-index: 2;
}

.bet-popover.placement-top::before {
  bottom: -7px;
  left: 50%;
  transform: translateX(-50%);
  border-width: 7px 7px 0 7px;
  border-color: var(--border-active) transparent transparent transparent;
  z-index: 1;
}

.pop-match {
  font-size: 9px;
  color: var(--text-muted);
  line-height: 1.3;
  margin-bottom: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pop-market {
  font-size: 8px;
  color: var(--text-muted);
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pop-pick-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  margin-bottom: 5px;
  border-radius: 3px;
  background: rgba(0, 102, 204, 0.06);
  border: 1px solid var(--border-active);
}

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

.odds {
  font-size: 10px;
  font-weight: 800;
  color: var(--primary);
  flex-shrink: 0;
}

.stake-label {
  display: block;
  font-size: 8px;
  color: var(--text-muted);
  margin-bottom: 2px;
}

.stake-input {
  width: 100%;
  box-sizing: border-box;
  padding: 4px 6px;
  margin-bottom: 5px;
  border-radius: 3px;
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text);
  font-size: 10px;
}

.stake-input:focus {
  border-color: var(--border-active);
  outline: none;
}

.est-row {
  display: flex;
  justify-content: space-between;
  font-size: 8px;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.est-val {
  color: var(--primary-light);
  font-weight: 700;
}

.pop-error {
  font-size: 8px;
  color: var(--danger);
  margin: 0 0 5px;
}

.pop-actions {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.pop-actions-row {
  display: flex;
  gap: 4px;
}

.pop-actions-row .btn-outline {
  flex: 1;
  width: auto;
  min-width: 0;
}

.btn-primary-gold {
  width: 100%;
  padding: 5px;
  border: none;
  border-radius: 3px;
  background: linear-gradient(180deg, var(--primary-light) 0%, var(--primary) 100%);
  color: #FFFFFF;
  font-size: 10px;
  font-weight: 800;
  cursor: pointer;
}

.btn-primary-gold:hover:not(:disabled) {
  opacity: 0.92;
}

.btn-primary-gold:disabled {
  opacity: 0.5;
  cursor: default;
}

.btn-outline {
  width: 100%;
  padding: 4px;
  border-radius: 3px;
  border: 1px solid var(--border-active);
  background: rgba(0, 102, 204, 0.05);
  color: var(--primary-light);
  font-size: 9px;
  font-weight: 700;
  cursor: pointer;
}

.btn-outline:hover {
  background: rgba(0, 102, 204, 0.12);
}
</style>
