<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useAdminLocale } from '../../composables/useAdminLocale';
import api from '../../api';
import type { AdminMatchDetail } from '../match-form';

const props = defineProps<{
  matchId: string;
}>();

const { t, locale } = useAdminLocale();

const LOCALES = [
  { code: 'zh-CN', label: '中文' },
  { code: 'en-US', label: 'English' },
  { code: 'ms-MY', label: 'Bahasa Melayu' },
] as const;

const UI_TEXT: Record<string, Record<string, string>> = {
  add: { 'zh-CN': '添加', 'en-US': 'Add', 'ms-MY': 'Tambah' },
  addMarket: { 'zh-CN': '增加盘口', 'en-US': 'Add market', 'ms-MY': 'Tambah pasaran' },
  addScore: { 'zh-CN': '新增比分', 'en-US': 'Add score', 'ms-MY': 'Tambah skor' },
  cancel: { 'zh-CN': '取消', 'en-US': 'Cancel', 'ms-MY': 'Batal' },
  close: { 'zh-CN': '关闭', 'en-US': 'Close', 'ms-MY': 'Tutup' },
  copyCreated: { 'zh-CN': '已复制盘口线', 'en-US': 'Market line duplicated', 'ms-MY': 'Garisan pasaran disalin' },
  copyMarket: { 'zh-CN': '复制盘口', 'en-US': 'Duplicate market', 'ms-MY': 'Salin pasaran' },
  copyMarketCreated: { 'zh-CN': '已复制盘口', 'en-US': 'Market duplicated', 'ms-MY': 'Pasaran disalin' },
  copyMarketShort: { 'zh-CN': '复制盘口', 'en-US': 'Duplicate', 'ms-MY': 'Salin pasaran' },
  copyLine: { 'zh-CN': '复制盘口线', 'en-US': 'Duplicate line', 'ms-MY': 'Salin garisan' },
  copyLineShort: { 'zh-CN': '复制线', 'en-US': 'Copy', 'ms-MY': 'Salin' },
  defaultTemplate: { 'zh-CN': '默认', 'en-US': 'Default', 'ms-MY': 'Lalai' },
  fillScoreRange: { 'zh-CN': '补齐 0-4 比分', 'en-US': 'Fill 0-4 scores', 'ms-MY': 'Lengkapkan skor 0-4' },
  hidden: { 'zh-CN': '玩家端隐藏', 'en-US': 'Hidden on player', 'ms-MY': 'Disembunyikan pemain' },
  lineValue: { 'zh-CN': '盘口线', 'en-US': 'Line', 'ms-MY': 'Garisan' },
  loadTemplate: { 'zh-CN': '载入模板', 'en-US': 'Load template', 'ms-MY': 'Muat templat' },
  marketDetails: { 'zh-CN': '赛事详情', 'en-US': 'Match details', 'ms-MY': 'Butiran perlawanan' },
  marketName: { 'zh-CN': '盘口名称', 'en-US': 'Market name', 'ms-MY': 'Nama pasaran' },
  marketToAdd: { 'zh-CN': '选择要添加的盘口', 'en-US': 'Choose a market to add', 'ms-MY': 'Pilih pasaran untuk ditambah' },
  marketUnavailable: { 'zh-CN': '暂不开放玩家端', 'en-US': 'Not available on player', 'ms-MY': 'Belum tersedia untuk pemain' },
  matchMarkets: { 'zh-CN': '赛事盘口', 'en-US': 'Match markets', 'ms-MY': 'Pasaran perlawanan' },
  moveDown: { 'zh-CN': '下移', 'en-US': 'Move down', 'ms-MY': 'Turun' },
  moveUp: { 'zh-CN': '上移', 'en-US': 'Move up', 'ms-MY': 'Naik' },
  odds: { 'zh-CN': '赔率', 'en-US': 'Odds', 'ms-MY': 'Odds' },
  parlay: { 'zh-CN': '串关', 'en-US': 'Parlay', 'ms-MY': 'Parlay' },
  playerVisible: { 'zh-CN': '玩家端显示', 'en-US': 'Show on player', 'ms-MY': 'Papar pemain' },
  promoLabel: { 'zh-CN': '促销标签', 'en-US': 'Promo label', 'ms-MY': 'Label promosi' },
  saveAsTemplate: { 'zh-CN': '保存为模板', 'en-US': 'Save as template', 'ms-MY': 'Simpan templat' },
  saveTemplateTitle: { 'zh-CN': '保存为盘口模板', 'en-US': 'Save as market template', 'ms-MY': 'Simpan sebagai templat pasaran' },
  selectMarket: { 'zh-CN': '从盘口池选择', 'en-US': 'Select from market pool', 'ms-MY': 'Pilih daripada kumpulan pasaran' },
  selectTemplate: { 'zh-CN': '选择模板', 'en-US': 'Select template', 'ms-MY': 'Pilih templat' },
  single: { 'zh-CN': '单关', 'en-US': 'Single', 'ms-MY': 'Tunggal' },
  status: { 'zh-CN': '状态', 'en-US': 'Status', 'ms-MY': 'Status' },
  statusClosed: { 'zh-CN': '已关闭', 'en-US': 'Closed', 'ms-MY': 'Ditutup' },
  statusOpen: { 'zh-CN': '可售', 'en-US': 'Open', 'ms-MY': 'Dibuka' },
  statusSuspended: { 'zh-CN': '暂停', 'en-US': 'Suspended', 'ms-MY': 'Digantung' },
  templateLoaded: { 'zh-CN': '模板已载入', 'en-US': 'Template loaded', 'ms-MY': 'Templat dimuatkan' },
  templateNameInput: { 'zh-CN': '请输入模板名称', 'en-US': 'Enter template name', 'ms-MY': 'Masukkan nama templat' },
  templateSaved: { 'zh-CN': '模板已保存', 'en-US': 'Template saved', 'ms-MY': 'Templat disimpan' },
  unsaved: { 'zh-CN': '未保存', 'en-US': 'Unsaved', 'ms-MY': 'Belum disimpan' },
};

type LocalizedText = Record<string, string>;

interface MarketDefinition {
  marketType: string;
  marketKey: string;
  period: string;
  sortOrder: number;
  defaultLineValue: number | null;
  allowSingle: boolean;
  allowParlay: boolean;
  showOnPlayer: boolean;
  settlementSupported: boolean;
  usesLineValue: boolean;
  renderType: 'standard' | 'correctScore' | 'unsupported';
  nameI18n: LocalizedText;
  selectionTemplate: Array<{
    code: string;
    name: string;
    nameI18n: LocalizedText;
    odds: number;
  }>;
}

interface SelectionRow {
  id?: string;
  selectionCode: string;
  selectionName: string;
  nameI18n: LocalizedText;
  odds: number;
  status: string;
  sortOrder: number;
}

interface MarketRow {
  id?: string;
  marketType: string;
  marketKey: string;
  period: string;
  lineValue: number | null;
  paramsJson: Record<string, unknown> | null;
  status: string;
  allowSingle: boolean;
  allowParlay: boolean;
  showOnPlayer: boolean;
  sortOrder: number;
  promoLabel: string;
  promoLabelI18n: LocalizedText;
  nameI18n: LocalizedText;
  selections: SelectionRow[];
}

interface MarketTemplateSummary {
  id: string;
  name: string;
  nameI18n: LocalizedText;
  isDefault: boolean;
  status: string;
}

const loading = ref(false);
const saving = ref(false);
const applying = ref(false);
const definitions = ref<MarketDefinition[]>([]);
const templates = ref<MarketTemplateSummary[]>([]);
const selectedTemplateId = ref('');
const markets = ref<MarketRow[]>([]);
const selectedMarketIndex = ref(0);
const scoreHome = ref(0);
const scoreAway = ref(0);
const addMarketVisible = ref(false);
const selectedDefinitionType = ref('');

const definitionMap = computed(() => new Map(definitions.value.map((d) => [d.marketType, d])));
const definitionOptions = computed(() => [...definitions.value].sort((a, b) => a.sortOrder - b.sortOrder));
const selectedMarket = computed(() => markets.value[selectedMarketIndex.value] ?? markets.value[0] ?? null);
const displayLocales = computed(() => {
  const zhLabels: Record<string, string> = {
    'zh-CN': '中文',
    'en-US': '英语',
    'ms-MY': '马来语',
  };
  return LOCALES.map((loc) => ({
    code: loc.code,
    label: locale.value === 'zh-CN' ? zhLabels[loc.code] : loc.label,
  }));
});
const dirty = ref(false);

function localized(input?: Record<string, string> | null, fallback = ''): LocalizedText {
  const out: LocalizedText = {};
  for (const loc of LOCALES) {
    const value = input?.[loc.code]?.trim();
    if (value) out[loc.code] = value;
  }
  if (!Object.keys(out).length && fallback) out['zh-CN'] = fallback;
  return out;
}

function resolveText(map?: LocalizedText | null, fallback = '') {
  const order = Array.from(new Set([locale.value, 'en-US', 'zh-CN', 'ms-MY']));
  for (const loc of order) {
    const value = map?.[loc]?.trim();
    if (value) return value;
  }
  return Object.values(map ?? {}).find((value) => value?.trim()) || fallback;
}

function ui(key: string) {
  return resolveText(UI_TEXT[key], key);
}

function statusLabel(status: string) {
  if (status === 'OPEN') return ui('statusOpen');
  if (status === 'SUSPENDED') return ui('statusSuspended');
  if (status === 'CLOSED') return ui('statusClosed');
  return status;
}

function ensureLocaleKeys(map: LocalizedText, fallback = '') {
  for (const loc of LOCALES) {
    if (map[loc.code] == null) map[loc.code] = loc.code === 'zh-CN' ? fallback : '';
  }
  return map;
}

function mapMarkets(detail: AdminMatchDetail) {
  markets.value = (detail.markets ?? []).map((m, index) => {
    const def = definitionMap.value.get(m.marketType);
    const nameI18n = ensureLocaleKeys(localized(m.nameI18n, def ? resolveText(def.nameI18n, m.marketType) : m.marketType), m.marketType);
    const promoLabelI18n = ensureLocaleKeys(localized(m.promoLabelI18n, m.promoLabel || ''), m.promoLabel || '');
    return {
      id: m.id,
      marketType: m.marketType,
      marketKey: m.marketKey || m.marketType,
      period: m.period,
      lineValue: m.lineValue,
      paramsJson: m.paramsJson ?? null,
      status: m.status,
      allowSingle: m.allowSingle ?? true,
      allowParlay: m.allowParlay ?? true,
      showOnPlayer: m.showOnPlayer ?? true,
      sortOrder: m.sortOrder ?? index,
      promoLabel: m.promoLabel ?? '',
      promoLabelI18n,
      nameI18n,
      selections: m.selections.map((s, sIndex) => ({
        id: s.id,
        selectionCode: s.selectionCode,
        selectionName: s.selectionName,
        nameI18n: ensureLocaleKeys(localized(s.nameI18n, s.selectionName), s.selectionName),
        odds: s.odds,
        status: s.status,
        sortOrder: s.sortOrder ?? sIndex,
      })),
    };
  });
  selectedMarketIndex.value = Math.min(selectedMarketIndex.value, Math.max(0, markets.value.length - 1));
  dirty.value = false;
}

function usesLineValue(marketType: string) {
  return definitionMap.value.get(marketType)?.usesLineValue ?? false;
}

function isLineMarket(market: MarketRow) {
  return usesLineValue(market.marketType);
}

async function loadDefinitionsAndTemplates() {
  const [{ data: defRes }, { data: tplRes }] = await Promise.all([
    api.get('/admin/market-definitions'),
    api.get('/admin/market-templates'),
  ]);
  definitions.value = defRes.data ?? [];
  templates.value = tplRes.data ?? [];
  selectedTemplateId.value =
    templates.value.find((tpl) => tpl.isDefault)?.id || templates.value[0]?.id || '';
}

async function load() {
  if (!props.matchId) return;
  loading.value = true;
  try {
    await loadDefinitionsAndTemplates();
    const { data } = await api.get(`/admin/matches/${props.matchId}`);
    mapMarkets(data.data as AdminMatchDetail);
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.load_failed'));
  } finally {
    loading.value = false;
  }
}

watch(
  () => props.matchId,
  () => {
    load();
  },
  { immediate: true },
);

function markDirty() {
  dirty.value = true;
}

function isCorrectScoreMarket(market: MarketRow) {
  return ['FT_CORRECT_SCORE', 'HT_CORRECT_SCORE', 'SH_CORRECT_SCORE'].includes(market.marketType);
}

function marketTitle(market: MarketRow) {
  const line = isLineMarket(market) && market.lineValue != null ? ` ${market.lineValue}` : '';
  return `${resolveText(market.nameI18n, market.marketType)}${line}`;
}

function definitionLabel(def: MarketDefinition) {
  const label = resolveText(def.nameI18n, def.marketType);
  return def.settlementSupported ? label : `${label}（${ui('marketUnavailable')}）`;
}

function templateLabel(tpl: MarketTemplateSummary) {
  const label = resolveText(tpl.nameI18n, tpl.name || '');
  return tpl.isDefault ? `${label}（${ui('defaultTemplate')}）` : label;
}

function marketMetaText(market: MarketRow) {
  const parts = [statusLabel(market.status)];
  if (isLineMarket(market) && market.lineValue != null) parts.push(`${ui('lineValue')} ${market.lineValue}`);
  if (!market.showOnPlayer) parts.push(ui('hidden'));
  return parts.join(' · ');
}

function selectionTitle(selection: SelectionRow) {
  const score = /^SCORE_(\d+)_(\d+)$/.exec(selection.selectionCode);
  if (score) return `${score[1]}:${score[2]}`;
  return resolveText(selection.nameI18n, selection.selectionName || selection.selectionCode);
}

function isScoreSelection(selection: SelectionRow) {
  return /^SCORE_\d+_\d+$/.test(selection.selectionCode);
}

function openAddMarketDialog() {
  selectedDefinitionType.value = definitionOptions.value[0]?.marketType ?? '';
  addMarketVisible.value = true;
}

function addSelectedMarket() {
  const def = definitionMap.value.get(selectedDefinitionType.value);
  if (!def) return;
  addMarket(def);
  addMarketVisible.value = false;
  selectedDefinitionType.value = '';
}

function addMarket(def: MarketDefinition) {
  const row: MarketRow = {
    marketType: def.marketType,
    marketKey: def.marketKey,
    period: def.period,
    lineValue: def.defaultLineValue,
    paramsJson: null,
    status: 'OPEN',
    allowSingle: def.allowSingle,
    allowParlay: def.allowParlay,
    showOnPlayer: def.showOnPlayer && def.settlementSupported,
    sortOrder: markets.value.length,
    promoLabel: '',
    promoLabelI18n: ensureLocaleKeys({}),
    nameI18n: ensureLocaleKeys(localized(def.nameI18n, def.marketType), def.marketType),
    selections: def.selectionTemplate.map((s, index) => ({
      selectionCode: s.code,
      selectionName: s.name,
      nameI18n: ensureLocaleKeys(localized(s.nameI18n, s.name), s.name),
      odds: Number(s.odds),
      status: 'OPEN',
      sortOrder: index,
    })),
  };
  markets.value.push(row);
  selectedMarketIndex.value = markets.value.length - 1;
  markDirty();
}

function copyLocalizedName(map: LocalizedText) {
  const suffix: Record<string, string> = {
    'zh-CN': ' 副本',
    'en-US': ' Copy',
    'ms-MY': ' Salinan',
  };
  const next = ensureLocaleKeys(localized(map, resolveText(map)));
  for (const loc of LOCALES) {
    const base = next[loc.code] || resolveText(next);
    next[loc.code] = `${base}${suffix[loc.code]}`;
  }
  return next;
}

function clonePlain<T>(value: T): T {
  return value == null ? value : JSON.parse(JSON.stringify(value));
}

function nextCopiedLineValue(source: MarketRow) {
  const used = new Set(
    markets.value
      .filter((market) => market.marketType === source.marketType && market.lineValue != null)
      .map((market) => Number(market.lineValue).toFixed(2)),
  );
  let candidate = Number(((source.lineValue ?? 0) + 0.25).toFixed(2));
  while (used.has(candidate.toFixed(2))) {
    candidate = Number((candidate + 0.25).toFixed(2));
  }
  return candidate;
}

function duplicateMarket(index: number) {
  const source = markets.value[index];
  if (!source) return;
  const shouldCopyLine = isLineMarket(source);
  const sourceCopy = clonePlain(source);
  const paramsCopy = clonePlain(source.paramsJson);
  const copy: MarketRow = {
    ...sourceCopy,
    id: undefined,
    lineValue: shouldCopyLine ? nextCopiedLineValue(source) : null,
    paramsJson: shouldCopyLine
      ? paramsCopy
      : { ...(paramsCopy ?? {}), copyId: `copy-${Date.now()}-${index}` },
    nameI18n: copyLocalizedName(source.nameI18n),
    sortOrder: markets.value.length,
    selections: source.selections.map((s) => ({ ...clonePlain(s), id: undefined })),
  };
  markets.value.splice(index + 1, 0, copy);
  selectedMarketIndex.value = index + 1;
  reorder();
  markDirty();
  ElMessage.success(ui(shouldCopyLine ? 'copyCreated' : 'copyMarketCreated'));
}

function removeMarket(index: number) {
  markets.value.splice(index, 1);
  selectedMarketIndex.value = Math.min(selectedMarketIndex.value, Math.max(0, markets.value.length - 1));
  reorder();
  markDirty();
}

function moveMarket(index: number, direction: -1 | 1) {
  const target = index + direction;
  if (target < 0 || target >= markets.value.length) return;
  const [row] = markets.value.splice(index, 1);
  markets.value.splice(target, 0, row);
  selectedMarketIndex.value = target;
  reorder();
  markDirty();
}

function reorder() {
  markets.value.forEach((m, index) => {
    m.sortOrder = index;
  });
}

function addScoreRange(market: MarketRow) {
  const existing = new Set(market.selections.map((s) => s.selectionCode));
  for (let home = 0; home <= 4; home += 1) {
    for (let away = 0; away <= 4; away += 1) {
      const code = `SCORE_${home}_${away}`;
      if (existing.has(code)) continue;
      market.selections.push({
        selectionCode: code,
        selectionName: `${home}-${away}`,
        nameI18n: ensureLocaleKeys({ 'zh-CN': `${home}-${away}`, 'en-US': `${home}-${away}`, 'ms-MY': `${home}-${away}` }),
        odds: 8,
        status: 'OPEN',
        sortOrder: market.selections.length,
      });
    }
  }
  markDirty();
}

function addScore(market: MarketRow) {
  const home = Math.max(0, Number(scoreHome.value) || 0);
  const away = Math.max(0, Number(scoreAway.value) || 0);
  const code = `SCORE_${home}_${away}`;
  if (market.selections.some((s) => s.selectionCode === code)) return;
  market.selections.push({
    selectionCode: code,
    selectionName: `${home}-${away}`,
    nameI18n: ensureLocaleKeys({ 'zh-CN': `${home}-${away}`, 'en-US': `${home}-${away}`, 'ms-MY': `${home}-${away}` }),
    odds: 8,
    status: 'OPEN',
    sortOrder: market.selections.length,
  });
  markDirty();
}

function removeSelection(market: MarketRow, index: number) {
  market.selections.splice(index, 1);
  market.selections.forEach((s, i) => {
    s.sortOrder = i;
  });
  markDirty();
}

function payloadMarkets() {
  return markets.value.map((m, index) => ({
    id: m.id,
    marketType: m.marketType,
    marketKey: m.marketKey,
    lineKey: null,
    period: m.period,
    lineValue: usesLineValue(m.marketType) ? m.lineValue : null,
    paramsJson: m.paramsJson,
    status: m.status,
    allowSingle: m.allowSingle,
    allowParlay: m.allowParlay,
    showOnPlayer: m.showOnPlayer,
    sortOrder: index,
    promoLabel: resolveText(m.promoLabelI18n, m.promoLabel),
    promoLabelI18n: localized(m.promoLabelI18n),
    nameI18n: localized(m.nameI18n, m.marketType),
    selections: m.selections.map((s, sIndex) => ({
      id: s.id,
      selectionCode: s.selectionCode,
      selectionName: resolveText(s.nameI18n, s.selectionName),
      nameI18n: localized(s.nameI18n, s.selectionName),
      odds: Number(s.odds),
      status: s.status,
      sortOrder: sIndex,
    })),
  }));
}

async function applyTemplate() {
  if (!selectedTemplateId.value) return;
  applying.value = true;
  try {
    await api.post(`/admin/matches/${props.matchId}/markets/apply-template`, {
      templateId: selectedTemplateId.value,
    });
    ElMessage.success(ui('templateLoaded'));
    await load();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.save_failed'));
  } finally {
    applying.value = false;
  }
}

async function saveAll() {
  const invalid = markets.value.flatMap((m) => m.selections).find((s) => !s.odds || s.odds <= 1);
  if (invalid) {
    ElMessage.warning(t('outright.err_odds_min'));
    return;
  }
  saving.value = true;
  try {
    await api.put(`/admin/matches/${props.matchId}/markets/bulk`, {
      markets: payloadMarkets(),
    });
    ElMessage.success(t('msg.saved'));
    await load();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.save_failed'));
  } finally {
    saving.value = false;
  }
}

async function saveAsTemplate() {
  let value = '';
  try {
    const result = await ElMessageBox.prompt(ui('templateNameInput'), ui('saveTemplateTitle'), {
      confirmButtonText: t('common.save'),
      cancelButtonText: t('common.cancel'),
      inputValue: `${resolveText({ 'zh-CN': '自定义足球盘口模板', 'en-US': 'Custom football market template', 'ms-MY': 'Templat pasaran bola sepak tersuai' })}`,
    });
    value = result.value?.trim() || '';
  } catch {
    return;
  }
  if (!value) return;
  saving.value = true;
  try {
    await api.post('/admin/market-templates', {
      name: value,
      nameI18n: { 'zh-CN': value },
      items: payloadMarkets(),
    });
    ElMessage.success(ui('templateSaved'));
    await loadDefinitionsAndTemplates();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.save_failed'));
  } finally {
    saving.value = false;
  }
}
</script>

<template>
  <div v-loading="loading" class="market-editor">
    <div class="market-toolbar">
      <div class="template-tools">
        <el-select v-model="selectedTemplateId" size="small" class="template-select" :placeholder="ui('selectTemplate')">
          <el-option
            v-for="tpl in templates"
            :key="tpl.id"
            :label="templateLabel(tpl)"
            :value="tpl.id"
          />
        </el-select>
        <el-button size="small" :loading="applying" @click="applyTemplate">{{ ui('loadTemplate') }}</el-button>
        <el-button size="small" @click="openAddMarketDialog">{{ ui('addMarket') }}</el-button>
        <el-button size="small" @click="saveAsTemplate">{{ ui('saveAsTemplate') }}</el-button>
      </div>
      <div class="save-tools">
        <el-tag v-if="dirty" size="small" type="warning">{{ ui('unsaved') }}</el-tag>
        <el-button size="small" type="primary" :loading="saving" @click="saveAll">
          {{ t('common.save') }}
        </el-button>
      </div>
    </div>

    <div class="editor-grid">
      <section class="market-list">
        <div class="section-title">{{ ui('matchMarkets') }}</div>
        <p v-if="!markets.length" class="empty-hint">{{ t('matchEditor.no_markets') }}</p>
        <div
          v-for="(market, index) in markets"
          :key="market.id || `${market.marketType}-${index}`"
          class="market-card"
          :class="{ active: selectedMarketIndex === index, hidden: !market.showOnPlayer }"
          @click="selectedMarketIndex = index"
        >
          <div class="card-main">
            <strong>{{ marketTitle(market) }}</strong>
            <span>{{ marketMetaText(market) }}</span>
          </div>
          <div class="card-actions" @click.stop>
            <el-button
              class="card-action-btn"
              size="small"
              text
              :title="ui('moveUp')"
              :disabled="index === 0"
              @click="moveMarket(index, -1)"
            >
              ↑
            </el-button>
            <el-button
              class="card-action-btn"
              size="small"
              text
              :title="ui('moveDown')"
              :disabled="index === markets.length - 1"
              @click="moveMarket(index, 1)"
            >
              ↓
            </el-button>
            <el-button
              class="card-action-btn copy-btn"
              size="small"
              text
              :title="ui(isLineMarket(market) ? 'copyLine' : 'copyMarket')"
              @click="duplicateMarket(index)"
            >
              {{ ui(isLineMarket(market) ? 'copyLineShort' : 'copyMarketShort') }}
            </el-button>
            <el-button class="card-action-btn" size="small" text type="danger" :title="ui('close')" @click="removeMarket(index)">
              {{ ui('close') }}
            </el-button>
          </div>
        </div>
      </section>

      <section v-if="selectedMarket" class="detail-panel">
        <div class="detail-head">
          <div>
            <div class="section-title">{{ ui('marketDetails') }}</div>
            <strong>{{ marketTitle(selectedMarket) }}</strong>
          </div>
          <el-switch
            v-model="selectedMarket.showOnPlayer"
            :active-text="ui('playerVisible')"
            @change="markDirty"
          />
        </div>

        <div class="form-grid">
          <label>
            <span>{{ ui('status') }}</span>
            <el-select v-model="selectedMarket.status" size="small" @change="markDirty">
              <el-option :label="ui('statusOpen')" value="OPEN" />
              <el-option :label="ui('statusSuspended')" value="SUSPENDED" />
              <el-option :label="ui('statusClosed')" value="CLOSED" />
            </el-select>
          </label>
          <label v-if="isLineMarket(selectedMarket)">
            <span>{{ ui('lineValue') }}</span>
            <el-input-number
              v-model="selectedMarket.lineValue"
              size="small"
              :step="0.25"
              controls-position="right"
              @change="markDirty"
            />
          </label>
          <label>
            <span>{{ ui('single') }}</span>
            <el-switch v-model="selectedMarket.allowSingle" @change="markDirty" />
          </label>
          <label>
            <span>{{ ui('parlay') }}</span>
            <el-switch v-model="selectedMarket.allowParlay" @change="markDirty" />
          </label>
        </div>

        <div class="locale-block">
          <div class="block-title">{{ ui('marketName') }}</div>
          <div class="locale-grid">
            <label v-for="loc in displayLocales" :key="loc.code">
              <span>{{ loc.label }}</span>
              <el-input v-model="selectedMarket.nameI18n[loc.code]" size="small" @input="markDirty" />
            </label>
          </div>
        </div>

        <div class="locale-block">
          <div class="block-title">{{ ui('promoLabel') }}</div>
          <div class="locale-grid">
            <label v-for="loc in displayLocales" :key="loc.code">
              <span>{{ loc.label }}</span>
              <el-input v-model="selectedMarket.promoLabelI18n[loc.code]" size="small" clearable @input="markDirty" />
            </label>
          </div>
        </div>

        <div v-if="isCorrectScoreMarket(selectedMarket)" class="score-tools">
          <el-button size="small" @click="addScoreRange(selectedMarket)">{{ ui('fillScoreRange') }}</el-button>
          <el-input-number v-model="scoreHome" size="small" :min="0" controls-position="right" />
          <span>-</span>
          <el-input-number v-model="scoreAway" size="small" :min="0" controls-position="right" />
          <el-button size="small" @click="addScore(selectedMarket)">{{ ui('addScore') }}</el-button>
        </div>

        <div class="selection-list">
          <div
            v-for="(sel, index) in selectedMarket.selections"
            :key="sel.id || `${sel.selectionCode}-${index}`"
            class="selection-row"
            :class="{ 'selection-row--score': isScoreSelection(sel) }"
          >
            <div class="selection-meta">
              <strong :title="sel.selectionCode">{{ selectionTitle(sel) }}</strong>
              <label class="selection-field selection-field--status">
                <span>{{ ui('status') }}</span>
                <el-select v-model="sel.status" size="small" @change="markDirty">
                  <el-option :label="ui('statusOpen')" value="OPEN" />
                  <el-option :label="ui('statusSuspended')" value="SUSPENDED" />
                  <el-option :label="ui('statusClosed')" value="CLOSED" />
                </el-select>
              </label>
              <label class="selection-field selection-field--odds">
                <span>{{ ui('odds') }}</span>
                <el-input-number
                  v-model="sel.odds"
                  size="small"
                  :min="1.01"
                  :step="0.01"
                  controls-position="right"
                  @change="markDirty"
                />
              </label>
              <el-button size="small" text type="danger" @click="removeSelection(selectedMarket, index)">{{ ui('close') }}</el-button>
            </div>
            <div v-if="!isScoreSelection(sel)" class="locale-grid compact">
              <label v-for="loc in displayLocales" :key="loc.code">
                <span>{{ loc.label }}</span>
                <el-input v-model="sel.nameI18n[loc.code]" size="small" @input="markDirty" />
              </label>
            </div>
          </div>
        </div>
      </section>
    </div>

    <el-dialog v-model="addMarketVisible" class="add-market-dialog" :title="ui('addMarket')" width="460px" destroy-on-close>
      <label class="add-market-field">
        <span>{{ ui('marketToAdd') }}</span>
        <el-select
          v-model="selectedDefinitionType"
          class="full-select"
          filterable
          :placeholder="ui('selectMarket')"
        >
          <el-option
            v-for="def in definitionOptions"
            :key="def.marketType"
            :label="definitionLabel(def)"
            :value="def.marketType"
          />
        </el-select>
      </label>
      <template #footer>
        <el-button @click="addMarketVisible = false">{{ ui('cancel') }}</el-button>
        <el-button type="primary" :disabled="!selectedDefinitionType" @click="addSelectedMarket">
          {{ ui('add') }}
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped>
.market-editor {
  background: #0a0a0a;
  height: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.market-toolbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border-bottom: 1px solid #242424;
  background: #0d0d0d;
}

.template-tools,
.save-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.template-select {
  width: 260px;
}

.editor-grid {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(320px, 420px) minmax(480px, 1fr);
  align-items: stretch;
  gap: 10px;
  padding: 10px 12px 16px;
  overflow: hidden;
}

.market-list,
.detail-panel {
  min-width: 0;
  min-height: 0;
  border: 1px solid #262626;
  border-radius: 8px;
  background: #111;
  padding: 10px;
  overflow: auto;
}

.section-title,
.block-title {
  color: #b8b8b8;
  font-size: 12px;
  font-weight: 700;
  margin-bottom: 8px;
}

.empty-hint {
  margin: 0;
  color: #777;
  font-size: 13px;
}

.market-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px;
  margin-bottom: 6px;
  border: 1px solid #282828;
  border-radius: 6px;
  background: #151515;
  cursor: pointer;
}

.market-card.active {
  border-color: #9f853d;
  background: #1a1710;
}

.market-card.hidden {
  opacity: 0.66;
}

.card-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
}

.card-main strong,
.detail-head strong {
  color: #f0f0f0;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-main span {
  color: #777;
  font-size: 11px;
}

.card-actions {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 30px 30px 46px 40px;
  gap: 2px;
  justify-content: end;
}

.card-action-btn {
  width: 100%;
  height: 26px;
  padding: 0;
  margin-left: 0 !important;
  font-size: 12px;
}

.copy-btn {
  font-size: 11px;
}

.detail-head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: flex-start;
  margin-bottom: 12px;
}

.form-grid,
.locale-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin-bottom: 12px;
}

.locale-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.locale-grid.compact {
  margin-bottom: 0;
}

label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  color: #aaa;
  font-size: 12px;
}

.locale-block {
  margin-bottom: 12px;
}

.score-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

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

.selection-row {
  padding: 8px;
  border: 1px solid #262626;
  border-radius: 6px;
  background: #141414;
}

.selection-meta {
  display: grid;
  grid-template-columns: minmax(90px, 1fr) 120px 132px 70px;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
}

.selection-row--score .selection-meta {
  margin-bottom: 0;
}

.selection-meta strong {
  color: #ddd;
  font-size: 12px;
}

.selection-field {
  gap: 3px;
}

.selection-field > span {
  color: #f3c969;
  font-size: 11px;
  font-weight: 800;
  line-height: 1;
}

.selection-field--status > span {
  color: #7dd3fc;
}

.selection-field--odds :deep(.el-input-number),
.selection-field--odds :deep(.el-input) {
  width: 100%;
}

.add-market-field {
  margin: 0;
}

.full-select {
  width: 100%;
}

@media (max-width: 720px) {
  .market-toolbar,
  .editor-grid,
  .form-grid,
  .locale-grid,
  .selection-meta {
    display: flex;
    flex-direction: column;
    align-items: stretch;
  }

  .template-select {
    width: 100%;
  }

  .market-list,
  .detail-panel {
    min-height: 320px;
  }
}

.market-editor {
  background: #ffffff;
  color: var(--text);
}

.market-toolbar {
  border-bottom-color: var(--border);
  background: #fbfaf7;
}

.market-list,
.detail-panel,
.market-card,
.selection-row {
  border-color: var(--border);
  background: #ffffff;
  color: var(--text);
}

.market-list,
.detail-panel {
  box-shadow: var(--shadow);
}

.section-title,
.block-title,
.card-main strong,
.detail-head strong,
.selection-meta strong {
  color: var(--text);
}

.empty-hint,
.card-main span,
label,
.selection-field > span {
  color: var(--text-muted);
}

.market-card:hover {
  border-color: #d5cfc3;
  background: var(--accent-hover);
}

.market-card.active {
  border-color: var(--primary);
  background: var(--accent-subtle);
}

.selection-row {
  background: #fbfaf7;
}

.selection-field > span {
  font-weight: 750;
}

.selection-field--status > span {
  color: var(--info-text);
}

@media (max-width: 720px) {
  .market-toolbar {
    align-items: stretch;
  }

  .template-tools,
  .save-tools {
    align-items: stretch;
    width: 100%;
  }
}
</style>
