<template>
  <div class="tournament-bracket-root">
    <h1>双败淘汰赛赛程树状图</h1>
    <div class="container">
      <!-- 比赛对战表 -->
      <div class="bracket-main">
        <!-- 胜者组 -->
        <div class="bracket-section">
          <h2>胜者组</h2>
          <div class="bracket-container" id="winnersBracketContainer">
            <div class="zoom-controls">
              <button class="zoom-btn" @click="zoomInWinners" title="放大 (+)">+</button>
              <button class="zoom-btn" @click="zoomOutWinners" title="缩小 (-)">-</button>
              <button class="zoom-btn" @click="resetZoomWinners" title="重置视图 (0)">⌂</button>
            </div>
            <div class="interaction-hint" v-if="winnersBracket.length > 0">
              <span>💡 拖拽移动 | 滚轮缩放 | 快捷键: +放大 -缩小 0重置</span>
            </div>
            <svg id="d3-winners-bracket" class="d3-bracket"></svg>
          </div>
        </div>

        <!-- 败者组 -->
        <div class="bracket-section">
          <h2>败者组</h2>
          <div class="bracket-container" id="losersBracketContainer">
            <div class="zoom-controls">
              <button class="zoom-btn" @click="zoomInLosers" title="放大 (+)">+</button>
              <button class="zoom-btn" @click="zoomOutLosers" title="缩小 (-)">-</button>
              <button class="zoom-btn" @click="resetZoomLosers" title="重置视图 (0)">⌂</button>
            </div>
            <div class="interaction-hint" v-if="losersBracket.length > 0">
              <span>💡 拖拽移动 | 滚轮缩放 | 快捷键: +放大 -缩小 0重置</span>
            </div>
            <svg id="d3-losers-bracket" class="d3-bracket"></svg>
          </div>
        </div>

        <!-- 决赛 -->
        <div class="bracket-section" v-if="finalMatch">
          <h2>决赛 (BO5)</h2>
          <div class="final-match-container">
            <div class="final-match">
              <div class="participant" :class="{ winner: finalMatch.decided && finalMatch.score1 > finalMatch.score2 }">
                <span>{{ finalMatch.participant1 ? finalMatch.participant1.name : '待定' }}</span>
                <input type="number" v-model.number="finalMatch.score1" :disabled="finalMatch.decided" placeholder="0">
              </div>
              <div class="participant" :class="{ winner: finalMatch.decided && finalMatch.score2 > finalMatch.score1 }">
                <span>{{ finalMatch.participant2 ? finalMatch.participant2.name : '待定' }}</span>
                <input type="number" v-model.number="finalMatch.score2" :disabled="finalMatch.decided" placeholder="0">
              </div>
              <button @click="confirmFinal" :disabled="finalMatch.decided">确认决赛比分</button>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 对话框组件 -->
    <SuccessDialog
      :visible="successDialog.visible"
      :message="successDialog.message"
      @close="successDialog.visible = false"
    />
    <ErrorDialog
      :visible="errorDialog.visible"
      :message="errorDialog.message"
      @close="errorDialog.visible = false"
    />
  </div>
</template>

<script setup>
import { ref, computed, onMounted, watch, nextTick, onUnmounted } from 'vue';
import { getSignUpResultList, updateSignUpResult, getTournamentList } from '@/api/tournament';
import * as d3 from 'd3';
import SuccessDialog from '@/components/SuccessDialog.vue';
import ErrorDialog from '@/components/ErrorDialog.vue';

const props = defineProps({
  tournamentId: {
    type: Number,
    required: true
  }
});

const tournament = ref({
  id: props.tournamentId,
  name: ''
});

const participants = ref([]);
const winnersBracket = ref([]);
const losersBracket = ref([]);
const finalMatch = ref(null);
const champion = ref(null);
const runnerUp = ref(null);
const thirdPlace = ref(null);
const loading = ref(false);

// Dialog state
const successDialog = ref({ visible: false, message: '' });
const errorDialog = ref({ visible: false, message: '' });

// Store zoom behaviors for both brackets
let winnersZoomBehavior = null;
let winnerssvgSelection = null;
let losersZoomBehavior = null;
let loserssvgSelection = null;



onMounted(() => {
  loadTournamentData();
  
  // Add keyboard shortcuts for zoom control
  const handleKeydown = (event) => {
    if (event.target.tagName === 'INPUT') return;
    switch(event.key) {
      case '+':
      case '=':
        event.preventDefault();
        if (event.ctrlKey) {
          zoomInLosers();
        } else {
          zoomInWinners();
        }
        break;
      case '-':
        event.preventDefault();
        if (event.ctrlKey) {
          zoomOutLosers();
        } else {
          zoomOutWinners();
        }
        break;
      case '0':
        event.preventDefault();
        if (event.ctrlKey) {
          resetZoomLosers();
        } else {
          resetZoomWinners();
        }
        break;
    }
  };
  
  document.addEventListener('keydown', handleKeydown);
  
  onUnmounted(() => {
    document.removeEventListener('keydown', handleKeydown);
    const winnersSvg = d3.select('#d3-winners-bracket');
    const losersSvg = d3.select('#d3-losers-bracket');
    if (winnersSvg.node()) winnersSvg.selectAll('*').remove();
    if (losersSvg.node()) losersSvg.selectAll('*').remove();
  });
});

const loadTournamentData = async () => {
  if (!props.tournamentId) return;
  
  loading.value = true;
  tournament.value.id = props.tournamentId;
  
  try {
    const tournaments = await getTournamentList();
    const selectedTournament = tournaments.find(t => t.id === props.tournamentId);
    if (selectedTournament) {
      tournament.value.name = selectedTournament.name;
    }
    
    const data = await getSignUpResultList();
    const filtered = data.filter(item => item.tournament_id == props.tournamentId);
    participants.value = filtered.map(item => ({
      id: item.id,
      name: item.sign_name,
      team_name: item.team_name || '',
      win: item.win || '0',
      lose: item.lose || '0',
      qq_code: item.qq_code || '',
      status: item.status || ''
    }));
    
    // 数据加载完成后自动生成赛程
    if (participants.value.length >= 4) {
      generateDoubleEliminationBracket();
    }
  } catch (err) {
    console.error('获取比赛数据失败:', err);
  } finally {
    loading.value = false;
  }
};

const generateDoubleEliminationBracket = () => {
  // 重置所有状态
  winnersBracket.value = [];
  losersBracket.value = [];
  finalMatch.value = null;
  champion.value = runnerUp.value = thirdPlace.value = null;
  
  const totalPlayers = participants.value.length;
  if (totalPlayers < 4) {
    errorDialog.value = { visible: true, message: '双败淘汰赛至少需要4名参赛者' };
    return;
  }
  
  // 计算需要的总槽位数（最近的2的幂）
  const totalSlots = Math.pow(2, Math.ceil(Math.log2(totalPlayers)));
  const shuffledParticipants = [...participants.value].sort(() => Math.random() - 0.5);
  
  // 填充到总槽位数，剩余位置为null（轮空）
  while (shuffledParticipants.length < totalSlots) {
    shuffledParticipants.push(null);
  }
  console.log(`总参赛者: ${totalPlayers}, 总槽位: ${totalSlots}`);
  // 生成胜者组第一轮
  const winnersRound1 = [];
  for (let i = 0; i < totalSlots; i += 2) {
    const p1 = shuffledParticipants[i];
    const p2 = shuffledParticipants[i + 1];
    // 处理轮空情况
    let winner = null;
    let decided = false;
    let score1 = 0;
    let score2 = 0;
    if (p1 && !p2) {
      // p1轮空直接晋级
      winner = p1;
      decided = true;
      score1 = 1;
      score2 = 0;
      console.log(`${p1.name} 轮空直接晋级`);
    } else if (!p1 && p2) {
      // p2轮空直接晋级
      winner = p2;
      decided = true;
      score1 = 0;
      score2 = 1;
      console.log(`${p2.name} 轮空直接晋级`);
    } else if (!p1 && !p2) {
      // 双轮空，无人晋级
      winner = null;
      decided = true;
      score1 = 0;
      score2 = 0;
      console.log('双轮空，无人晋级');
    }
    
    winnersRound1.push({
      participant1: p1,
      participant2: p2,
      score1: score1,
      score2: score2,
      decided: decided,
      winner: winner,
      loser: null // 轮空情况下没有败者
    });
  }
  winnersBracket.value = [winnersRound1];
  // 预生成胜者组的所有轮次结构
  let currentRoundSize = Math.ceil(totalSlots / 2);
  while (currentRoundSize > 1) {
    currentRoundSize = Math.ceil(currentRoundSize / 2);
    const nextRound = [];
    for (let i = 0; i < currentRoundSize; i++) {
      nextRound.push({
        participant1: null,
        participant2: null,
        score1: 0,
        score2: 0,
        decided: false,
        winner: null,
        loser: null
      });
    }
    if (currentRoundSize > 0) {
      winnersBracket.value.push(nextRound);
    }
  }
  console.log(`胜者组轮次数: ${winnersBracket.value.length}`);
  // 预生成败者组结构
  generateLosersBracketStructure(totalPlayers);
  // 处理第一轮的轮空晋级
  winnersRound1.forEach((match, index) => {
    if (match.decided && match.winner) {
      // 轮空获胜者直接晋级到胜者组下一轮
      handleWinnersBracketAdvancement(match, 0, index);
    }
  });
  
  nextTick(() => {
    drawD3WinnersBracket();
    drawD3LosersBracket();
  });
};

// 生成败者组结构 - 根据标准双败逻辑
const generateLosersBracketStructure = (totalPlayers) => {
  const winnersRounds = winnersBracket.value.length;
  losersBracket.value = [];
  
  // 标准双败淘汰赛败者组结构：
  // 计算实际的参赛者数量和轮空数量
  const totalSlots = Math.pow(2, Math.ceil(Math.log2(totalPlayers)));
  const byeCount = totalSlots - totalPlayers;
  
  console.log(`总人数: ${totalPlayers}, 总槽位: ${totalSlots}, 轮空数: ${byeCount}`);
  
  // 败者组轮次数计算：考虑轮空影响
  let losersRounds;
  if (winnersRounds === 1) {
    losersRounds = 0; // 2人比赛无败者组
  } else if (totalPlayers <= 4) {
    losersRounds = 2; // 4人或5人比赛，败者组2轮
  } else {
    losersRounds = (winnersRounds - 1) * 2;
  }
  
  console.log(`胜者组轮次: ${winnersRounds}, 败者组轮次: ${losersRounds}`);
  
  if (losersRounds === 0) {
    console.log('只有2人比赛，不需要败者组');
    return;
  }
  
  for (let round = 0; round < losersRounds; round++) {
    const roundMatches = [];
    let matchCount = 1;
    // 败者组比赛数量规律：
    // LR1: 1场 (胜者组第1轮败者们对战，但要考虑轮空)
    // LR2: 1场 (LR1胜者 vs 胜者组第2轮败者)
    if (round === 0) {
      // 第一轮败者组：计算实际的败者数量
      const firstRoundLosers = Math.floor(totalPlayers / 2); // 第一轮实际对战产生的败者数
      if (firstRoundLosers >= 2) {
        matchCount = 1; // 至少需要1场比赛
      } else if (firstRoundLosers === 1) {
        // 如果只有1个败者，这个败者直接晋级到下一轮
        matchCount = 0; // 这轮不需要比赛
      }
    } else {
      matchCount = 1; // 其他轮次通常是1场
    }
    // 确保至少有0场比赛（可能某轮不需要比赛）
    matchCount = Math.max(0, matchCount);
    if (matchCount > 0) {
      for (let i = 0; i < matchCount; i++) {
        roundMatches.push({
          participant1: null,
          participant2: null,
          score1: 0,
          score2: 0,
          decided: false,
          winner: null,
          loser: null
        });
      }
      losersBracket.value.push(roundMatches);
      console.log(`败者组第${round + 1}轮: ${matchCount}场比赛`);
    } else {
      console.log(`败者组第${round + 1}轮: 跳过（无需比赛）`);
    }
  }
};

// D3.js drawing functions for winners bracket
const drawD3WinnersBracket = () => {
  const svg = d3.select('#d3-winners-bracket');
  svg.selectAll('*').remove();
  
  if (!winnersBracket.value.length) return;
  
  drawD3Bracket(svg, winnersBracket.value, 'winnersBracketContainer', 'winners');
};

// D3.js drawing functions for losers bracket  
const drawD3LosersBracket = () => {
  const svg = d3.select('#d3-losers-bracket');
  svg.selectAll('*').remove();
  
  if (!losersBracket.value.length) return;
  
  drawD3Bracket(svg, losersBracket.value, 'losersBracketContainer', 'losers');
};
// Generic D3.js bracket drawing function
const drawD3Bracket = (svg, bracket, containerId, bracketType) => {
  const margin = { top: 40, right: 40, bottom: 40, left: 40 };
  const matchWidth = 180;
  const matchHeight = 120;
  const roundGap = 50;
  const matchGap = 20;

  const rounds = bracket.length;
  const maxMatchesInRound = Math.max(...bracket.map(round => round.length));

  const totalWidth = rounds * (matchWidth + roundGap) + margin.left + margin.right;
  const totalHeight = maxMatchesInRound * (matchHeight + matchGap) + margin.top + margin.bottom;

  const container = document.getElementById(containerId);
  const containerWidth = container.clientWidth - 30;
  const containerHeight = container.clientHeight - 30;

  svg
    .attr('width', containerWidth)
    .attr('height', containerHeight)
    .attr('viewBox', `0 0 ${totalWidth} ${totalHeight}`)
    .attr('preserveAspectRatio', 'xMidYMid meet');

  // Create zoom behavior
  const zoom = d3.zoom()
    .scaleExtent([0.1, 3])
    .on('zoom', (event) => {
      g.attr('transform', `translate(${margin.left},${margin.top}) ${event.transform}`);
    });

  // Store references for external control
  if (bracketType === 'winners') {
    winnersZoomBehavior = zoom;
    winnerssvgSelection = svg;
  } else {
    losersZoomBehavior = zoom;
    loserssvgSelection = svg;
  }

  svg.call(zoom);

  svg.style('cursor', 'grab')
    .on('mousedown', () => svg.style('cursor', 'grabbing'))
    .on('mouseup', () => svg.style('cursor', 'grab'));

  const g = svg.append('g')
    .attr('transform', `translate(${margin.left},${margin.top})`);

  // Draw matches for each round
  bracket.forEach((round, roundIndex) => {
    const roundX = roundIndex * (matchWidth + roundGap);
    const roundStartY = (maxMatchesInRound * (matchHeight + matchGap) - round.length * (matchHeight + matchGap)) / 2;

    // Round title
    g.append('text')
      .attr('x', roundX + matchWidth / 2)
      .attr('y', -10)
      .attr('text-anchor', 'middle')
      .attr('class', 'round-title')
      .style('font-weight', 'bold')
      .style('font-size', '14px')
      .text(`第 ${roundIndex + 1} 轮`);

    round.forEach((match, matchIndex) => {
      const matchY = roundStartY + matchIndex * (matchHeight + matchGap);
      
      // Match container
      const matchGroup = g.append('g')
        .attr('class', 'match-group')
        .attr('data-match-id', `${bracketType}-${roundIndex}-${matchIndex}`);

      // Match background
      matchGroup.append('rect')
        .attr('x', roundX)
        .attr('y', matchY)
        .attr('width', matchWidth)
        .attr('height', matchHeight)
        .attr('class', 'match-bg')
        .style('fill', '#fafafa')
        .style('stroke', '#ddd')
        .style('stroke-width', 1)
        .style('rx', 6);

      // Store match position for connection drawing
      match._x = roundX + matchWidth;
      match._y = matchY + matchHeight / 2;
      match._roundIndex = roundIndex;
      match._matchIndex = matchIndex;

      // Participant 1
      const p1Group = matchGroup.append('g')
        .attr('class', 'participant-group')
        .attr('transform', `translate(${roundX + 10}, ${matchY + 20})`);

      p1Group.append('rect')
        .attr('width', matchWidth - 20)
        .attr('height', 30)
        .style('fill', match.winner && match.participant1 && match.winner.id === match.participant1.id ? '#e8f5e8' : '#fff')
        .style('stroke', '#ccc')
        .style('rx', 3);

      p1Group.append('text')
        .attr('x', 8)
        .attr('y', 20)
        .style('font-size', '12px')
        .style('font-weight', match.winner && match.participant1 && match.winner.id === match.participant1.id ? 'bold' : 'normal')
        .style('fill', match.winner && match.participant1 && match.winner.id === match.participant1.id ? '#2a7f2a' : '#333')
        .text(match.participant1 ? match.participant1.name : '轮空');

      // Score input for participant 1
      if (match.participant1 && match.participant2 && !match.decided) {
        const scoreInput1 = p1Group.append('foreignObject')
          .attr('x', matchWidth - 60)
          .attr('y', 5)
          .attr('width', 40)
          .attr('height', 20);

        scoreInput1.append('xhtml:input')
          .attr('type', 'number')
          .attr('min', '0')
          .style('width', '35px')
          .style('height', '18px')
          .style('font-size', '11px')
          .style('text-align', 'center')
          .style('border', '1px solid #aaa')
          .style('border-radius', '3px')
          .property('value', match.score1 || '')
          .on('input', function() {
            match.score1 = +this.value;
          });
      } else if (match.decided) {
        p1Group.append('text')
          .attr('x', matchWidth - 35)
          .attr('y', 20)
          .style('font-size', '12px')
          .style('font-weight', 'bold')
          .style('text-anchor', 'middle')
          .text(match.score1 || 0);
      }

      // Participant 2
      const p2Group = matchGroup.append('g')
        .attr('class', 'participant-group')
        .attr('transform', `translate(${roundX + 10}, ${matchY + 55})`);

      p2Group.append('rect')
        .attr('width', matchWidth - 20)
        .attr('height', 30)
        .style('fill', match.winner && match.participant2 && match.winner.id === match.participant2.id ? '#e8f5e8' : '#fff')
        .style('stroke', '#ccc')
        .style('rx', 3);

      p2Group.append('text')
        .attr('x', 8)
        .attr('y', 20)
        .style('font-size', '12px')
        .style('font-weight', match.winner && match.participant2 && match.winner.id === match.participant2.id ? 'bold' : 'normal')
        .style('fill', match.winner && match.participant2 && match.winner.id === match.participant2.id ? '#2a7f2a' : '#333')
        .text(match.participant2 ? match.participant2.name : '轮空');

      // Score input for participant 2
      if (match.participant1 && match.participant2 && !match.decided) {
        const scoreInput2 = p2Group.append('foreignObject')
          .attr('x', matchWidth - 60)
          .attr('y', 5)
          .attr('width', 40)
          .attr('height', 20);

        scoreInput2.append('xhtml:input')
          .attr('type', 'number')
          .attr('min', '0')
          .style('width', '35px')
          .style('height', '18px')
          .style('font-size', '11px')
          .style('text-align', 'center')
          .style('border', '1px solid #aaa')
          .style('border-radius', '3px')
          .property('value', match.score2 || '')
          .on('input', function() {
            match.score2 = +this.value;
          });
      } else if (match.decided) {
        p2Group.append('text')
          .attr('x', matchWidth - 35)
          .attr('y', 20)
          .style('font-size', '12px')
          .style('font-weight', 'bold')
          .style('text-anchor', 'middle')
          .text(match.score2 || 0);
      }

      // Confirm score button
      if (match.participant1 && match.participant2 && !match.decided) {
        const buttonGroup = matchGroup.append('g')
          .attr('transform', `translate(${roundX + 10}, ${matchY + 90})`);

        const button = buttonGroup.append('rect')
          .attr('width', matchWidth - 20)
          .attr('height', 25)
          .style('fill', '#007acc')
          .style('stroke', 'none')
          .style('rx', 4)
          .style('cursor', 'pointer')
          .on('click', () => confirmScore(match, bracketType, roundIndex, matchIndex));

        buttonGroup.append('text')
          .attr('x', (matchWidth - 20) / 2)
          .attr('y', 17)
          .style('fill', 'white')
          .style('font-size', '12px')
          .style('font-weight', 'bold')
          .style('text-anchor', 'middle')
          .style('pointer-events', 'none')
          .text('确认比分');
      }
    });
  });

  // Draw connections between matches
  drawD3Connections(g, bracket, bracketType, matchWidth, matchHeight, roundGap, matchGap);
};

// Draw connections between matches in brackets
const drawD3Connections = (g, bracket, bracketType, matchWidth, matchHeight, roundGap, matchGap) => {
  // Draw connections for adjacent rounds
  for (let roundIndex = 0; roundIndex < bracket.length - 1; roundIndex++) {
    const currentRound = bracket[roundIndex];
    const nextRound = bracket[roundIndex + 1];
    
    if (!nextRound) continue;
    
    // Connect matches from current round to next round
    for (let matchIndex = 0; matchIndex < currentRound.length; matchIndex += 2) {
      const match1 = currentRound[matchIndex];
      const match2 = currentRound[matchIndex + 1];
      const nextMatchIndex = Math.floor(matchIndex / 2);
      const nextMatch = nextRound[nextMatchIndex];
      
      if (!nextMatch || nextMatch._x === undefined) continue;
      
      // Draw connection from match1 to next match
      if (match1 && match1.decided && match1._x !== undefined) {
        const startX = match1._x;
        const startY = match1._y;
        const endX = nextMatch._x - matchWidth;
        const endY = nextMatch._y;
        const midX = startX + roundGap / 2;
        
        g.append('path')
          .attr('d', `M${startX},${startY} L${midX},${startY} L${midX},${endY} L${endX},${endY}`)
          .style('stroke', '#4CAF50')
          .style('stroke-width', 2)
          .style('fill', 'none')
          .style('opacity', 0.8);
      }
      
      // Draw connection from match2 to next match (if match2 exists)
      if (match2 && match2.decided && match2._x !== undefined) {
        const startX = match2._x;
        const startY = match2._y;
        const endX = nextMatch._x - matchWidth;
        const endY = nextMatch._y;
        const midX = startX + roundGap / 2;
        
        g.append('path')
          .attr('d', `M${startX},${startY} L${midX},${startY} L${midX},${endY} L${endX},${endY}`)
          .style('stroke', '#4CAF50')
          .style('stroke-width', 2)
          .style('fill', 'none')
          .style('opacity', 0.8);
      }
    }
  }
};

const confirmScore = async (match, bracketType, rIndex, mIndex) => {
  if (match.score1 === match.score2) {
    errorDialog.value = { visible: true, message: '不能平局' };
    return;
  }
  match.decided = true;
  match.winner = match.score1 > match.score2 ? match.participant1 : match.participant2;
  match.loser = match.score1 > match.score2 ? match.participant2 : match.participant1;
  
  // 更新选手胜负场数据
  await updatePlayerStats(match.participant1, match.participant2, match.score1, match.score2, bracketType);

  if (bracketType === 'winners') {
    // 胜者组逻辑
    handleWinnersBracketAdvancement(match, rIndex, mIndex);
  } else {
    // 败者组逻辑
    handleLosersBracketAdvancement(match, rIndex, mIndex);
  }

  // 检查是否需要生成决赛
  checkForFinalMatch();

  // Redraw brackets
  nextTick(() => {
    drawD3WinnersBracket();
    drawD3LosersBracket();
  });
};

// 处理胜者组晋级逻辑
const handleWinnersBracketAdvancement = (match, rIndex, mIndex) => {
  // 胜者晋级到胜者组下一轮
  if (rIndex + 1 < winnersBracket.value.length) {
    const nextRound = winnersBracket.value[rIndex + 1];
    const nextMatchIndex = Math.floor(mIndex / 2);
    const isUpperSlot = mIndex % 2 === 0;
    
    if (nextRound[nextMatchIndex]) {
      if (isUpperSlot) {
        nextRound[nextMatchIndex].participant1 = match.winner;
      } else {
        nextRound[nextMatchIndex].participant2 = match.winner;
      }
      
      console.log(`胜者组第${rIndex + 1}轮胜者${match.winner ? match.winner.name : 'null'}晋级到第${rIndex + 2}轮`);
    }
  }
  
  // 败者进入败者组（只有在有败者的情况下）
  if (match.loser) {
    sendLoserToLosersBracket(match.loser, rIndex);
  } else if (match.participant1 && match.participant2) {
    // 只有在两个参赛者都存在的情况下才有败者
    const loser = match.winner === match.participant1 ? match.participant2 : match.participant1;
    sendLoserToLosersBracket(loser, rIndex);
  }
  // 轮空情况下不产生败者，不需要送入败者组
};

// 处理败者组晋级逻辑
const handleLosersBracketAdvancement = (match, rIndex, mIndex) => {
  // 败者组胜者晋级到败者组下一轮
  if (rIndex + 1 < losersBracket.value.length) {
    const nextRound = losersBracket.value[rIndex + 1];
    
    // 败者组的晋级逻辑:
    // LR1 胜者 → LR2 的 participant1
    // LR2 需要等待胜者组第2轮败者作为 participant2
    
    if (nextRound.length > 0) {
      const nextMatch = nextRound[0]; // 败者组通常每轮只有1场比赛
      
      if (rIndex % 2 === 0) {
        // 偶数轮 (LR1, LR3...): 胜者进入下一轮作为participant1
        if (!nextMatch.participant1) {
          nextMatch.participant1 = match.winner;
          console.log(`败者组第${rIndex + 1}轮胜者${match.winner.name}晋级到第${rIndex + 2}轮作为participant1`);
        }
      } else {
        // 奇数轮 (LR2, LR4...): 胜者进入下一轮作为participant2
        if (!nextMatch.participant2) {
          nextMatch.participant2 = match.winner;
          console.log(`败者组第${rIndex + 1}轮胜者${match.winner.name}晋级到第${rIndex + 2}轮作为participant2`);
        }
      }
    }
  }
  // 败者组的败者直接淘汰，不做处理
};

// 将败者送入败者组的正确位置
const sendLoserToLosersBracket = (loser, winnersRoundIndex) => {
  if (!loser || losersBracket.value.length === 0) return;
  
  console.log(`处理败者: ${loser.name}, 来自胜者组第${winnersRoundIndex + 1}轮`);
  
  // 标准双败淘汰赛逻辑：
  // 胜者组第1轮败者 → 败者组第1轮 (与其他第1轮败者对战)
  // 胜者组第2轮败者 → 败者组第2轮 (与败者组第1轮胜者对战)
  
  let targetRound;
  if (winnersRoundIndex === 0) {
    // 胜者组第1轮败者进入败者组第1轮
    targetRound = 0;
    
    // 特殊情况：如果败者组第1轮没有比赛（只有1个败者），直接晋级到第2轮
    if (losersBracket.value.length > 0 && losersBracket.value[0].length === 0) {
      targetRound = 1;
      console.log(`败者组第1轮无比赛，${loser.name}直接进入第2轮`);
    }
  } else {
    // 胜者组第N轮败者进入败者组第(2N-1)轮
    targetRound = winnersRoundIndex * 2 - 1;
    
    // 确保不超过败者组轮次数
    if (targetRound >= losersBracket.value.length) {
      targetRound = losersBracket.value.length - 1;
    }
  }
  
  console.log(`将${loser.name}放入败者组第${targetRound + 1}轮`);
  
  if (targetRound < losersBracket.value.length && targetRound >= 0 && losersBracket.value[targetRound]) {
    const losersRound = losersBracket.value[targetRound];
    
    if (losersRound.length === 0) {
      // 如果这一轮没有比赛，说明败者直接晋级到下一轮
      console.log(`败者组第${targetRound + 1}轮无比赛，${loser.name}继续晋级`);
      if (targetRound + 1 < losersBracket.value.length) {
        sendLoserToLosersBracket(loser, winnersRoundIndex + 1);
      }
      return;
    }
    
    // 找到第一个空位置放入败者
    for (let i = 0; i < losersRound.length; i++) {
      const match = losersRound[i];
      if (!match.participant1) {
        match.participant1 = loser;
        console.log(`${loser.name}被放入败者组第${targetRound + 1}轮第${i + 1}场的participant1位置`);
        break;
      } else if (!match.participant2) {
        match.participant2 = loser;
        console.log(`${loser.name}被放入败者组第${targetRound + 1}轮第${i + 1}场的participant2位置`);
        break;
      }
    }
  }
};

// 检查是否需要生成决赛
const checkForFinalMatch = () => {
  const lastWinnersRound = winnersBracket.value[winnersBracket.value.length - 1];
  const lastLosersRound = losersBracket.value[losersBracket.value.length - 1];
  
  if (lastWinnersRound && lastWinnersRound.length === 1 && lastWinnersRound[0].decided &&
      lastLosersRound && lastLosersRound.length === 1 && lastLosersRound[0].decided) {
    
    const winnersChampion = lastWinnersRound[0].winner;
    const losersChampion = lastLosersRound[0].winner;
    
    if (winnersChampion && losersChampion && !finalMatch.value) {
      finalMatch.value = {
        participant1: winnersChampion,
        participant2: losersChampion,
        score1: 0,
        score2: 0,
        decided: false,
        winner: null,
        loser: null
      };
    }
  }
};

// Zoom control functions for winners bracket
const zoomInWinners = () => {
  if (winnerssvgSelection && winnersZoomBehavior) {
    winnerssvgSelection.transition().duration(300).call(
      winnersZoomBehavior.scaleBy, 1.5
    );
  }
};

const zoomOutWinners = () => {
  if (winnerssvgSelection && winnersZoomBehavior) {
    winnerssvgSelection.transition().duration(300).call(
      winnersZoomBehavior.scaleBy, 1 / 1.5
    );
  }
};

const resetZoomWinners = () => {
  if (winnerssvgSelection && winnersZoomBehavior) {
    winnerssvgSelection.transition().duration(500).call(
      winnersZoomBehavior.transform,
      d3.zoomIdentity
    );
  }
};

// Zoom control functions for losers bracket
const zoomInLosers = () => {
  if (loserssvgSelection && losersZoomBehavior) {
    loserssvgSelection.transition().duration(300).call(
      losersZoomBehavior.scaleBy, 1.5
    );
  }
};

const zoomOutLosers = () => {
  if (loserssvgSelection && losersZoomBehavior) {
    loserssvgSelection.transition().duration(300).call(
      losersZoomBehavior.scaleBy, 1 / 1.5
    );
  }
};

const resetZoomLosers = () => {
  if (loserssvgSelection && losersZoomBehavior) {
    loserssvgSelection.transition().duration(500).call(
      losersZoomBehavior.transform,
      d3.zoomIdentity
    );
  }
};

const confirmFinal = async () => {
  const match = finalMatch.value;
  if (!match || match.score1 === match.score2) {
    errorDialog.value = { visible: true, message: '决赛不能平局' };
    return;
  }
  
  match.decided = true;
  match.winner = match.score1 > match.score2 ? match.participant1 : match.participant2;
  match.loser = match.score1 > match.score2 ? match.participant2 : match.participant1;

  // 更新决赛选手胜负场数据
  await updatePlayerStats(match.participant1, match.participant2, match.score1, match.score2, 'final');

  // 双败淘汰赛的特殊规则：bracket reset
  // 如果败者组冠军（participant2）在决赛中获胜，则需要重置比赛
  if (match.winner === match.participant2) {
    // Bracket Reset: 败者组冠军获胜，需要再打一场
    successDialog.value = { visible: true, message: '败者组冠军获胜！比赛重置，将进行最终决赛！' };
    finalMatch.value = {
      participant1: match.participant2, // 原败者组冠军
      participant2: match.participant1, // 原胜者组冠军
      score1: 0,
      score2: 0,
      decided: false,
      winner: null,
      loser: null,
      isResetMatch: true // 标记这是重置后的比赛
    };
    return;
  }

  // 如果胜者组冠军获胜，或者这是重置后的比赛，则确定最终名次
  champion.value = match.winner.name || match.winner;
  runnerUp.value = match.loser.name || match.loser;
  
  // 在双败赛制中，季军是败者组决赛的败者
  if (losersBracket.value.length > 0) {
    const lastLoserRound = losersBracket.value[losersBracket.value.length - 1];
    if (lastLoserRound && lastLoserRound.length > 0) {
      const lastLoserMatch = lastLoserRound[lastLoserRound.length - 1];
      if (lastLoserMatch && lastLoserMatch.loser) {
        // 如果是重置比赛，季军应该是原来的胜者组冠军（如果败者组冠军最终获胜）
        if (match.isResetMatch && match.winner === match.participant1) {
          thirdPlace.value = match.participant2.name || match.participant2;
        } else {
          thirdPlace.value = lastLoserMatch.loser.name || lastLoserMatch.loser;
        }
      }
    }
  }

  // 备选季军逻辑
  if (!thirdPlace.value && losersBracket.value.length > 1) {
    const semiLoserRound = losersBracket.value[losersBracket.value.length - 2];
    if (semiLoserRound && semiLoserRound.length > 0) {
      const semiLoserMatch = semiLoserRound[semiLoserRound.length - 1];
      if (semiLoserMatch && semiLoserMatch.loser) {
        thirdPlace.value = semiLoserMatch.loser.name || semiLoserMatch.loser;
      }
    }
  }

  // 更新最终排名状态
  try {
    const latestData = await getSignUpResultList();
    
    // 更新冠军状态
    const championData = latestData.find(item => item.sign_name === champion.value);
    if (championData) {
      await updateSignUpResult(championData.id, {
        ...championData,
        status: 'win',
        rank: '1'
      });
    }
    
    // 更新亚军状态
    const runnerUpData = latestData.find(item => item.sign_name === runnerUp.value);
    if (runnerUpData) {
      await updateSignUpResult(runnerUpData.id, {
        ...runnerUpData,
        status: 'lose',
        rank: '2'
      });
    }
    
    // 更新季军状态
    if (thirdPlace.value && thirdPlace.value !== '待定') {
      const thirdPlaceData = latestData.find(item => item.sign_name === thirdPlace.value);
      if (thirdPlaceData) {
        await updateSignUpResult(thirdPlaceData.id, {
          ...thirdPlaceData,
          status: 'lose',
          rank: '3'
        });
      }
    }
  } catch (err) {
    console.error('更新最终排名失败:', err);
  }
};

// 更新选手胜负场数据到API
const updatePlayerStats = async (p1, p2, s1, s2, bracketType = 'winners') => {
  if (!p1 || !p2) return;
  
  try {
    // 先获取最新的报名数据
    const latestData = await getSignUpResultList();
    const p1Latest = latestData.find(item => item.id === p1.id);
    const p2Latest = latestData.find(item => item.id === p2.id);
    
    if (!p1Latest || !p2Latest) return;
    
    // 计算积分 - 胜者组获得全额积分，败者组获得一半积分
    const pointMultiplier = bracketType === 'winners' ? 1 : 0.5;
    const winPoints = s1 > s2 ? 1 * pointMultiplier : 0;
    const losePoints = s2 > s1 ? 1 * pointMultiplier : 0;
    
    // 更新选手1的数据
    const p1Data = {
      tournament_id: props.tournamentId,
      tournament_name: tournament.value.name,
      team_name: p1.team_name || '',
      sign_name: p1.name,
      win: String(Number(p1Latest.win || 0) + s1),
      lose: String(Number(p1Latest.lose || 0) + s2),
      status: s1 > s2 ? 'win' : 'lose',
      qq_code: p1.qq_code || '',
      points: String(Number(p1Latest.points || 0) + (s1 > s2 ? winPoints : 0)),
      bracket_type: bracketType
    };
    
    // 更新选手2的数据
    const p2Data = {
      tournament_id: props.tournamentId,
      tournament_name: tournament.value.name,
      team_name: p2.team_name || '',
      sign_name: p2.name,
      win: String(Number(p2Latest.win || 0) + s2),
      lose: String(Number(p2Latest.lose || 0) + s1),
      status: s2 > s1 ? 'win' : 'lose',
      qq_code: p2.qq_code || '',
      points: String(Number(p2Latest.points || 0) + (s2 > s1 ? winPoints : 0)),
      bracket_type: bracketType
    };
    
    // 调用API更新数据
    await updateSignUpResult(p1.id, p1Data);
    await updateSignUpResult(p2.id, p2Data);
    
    // 刷新参赛者列表
    await loadTournamentData();
  } catch (err) {
    console.error('更新选手数据失败:', err);
    errorDialog.value = { visible: true, message: '更新选手数据失败' };
  }
};

// Watch for prop changes
watch(() => props.tournamentId, (newId) => {
  if (newId) {
    loadTournamentData();
  }
}, { immediate: false });

// Watch for bracket changes to redraw
watch([winnersBracket, losersBracket], () => {
  nextTick(() => {
    drawD3WinnersBracket();
    drawD3LosersBracket();
  });
}, { deep: true });
</script>

<style scoped>
h1 {
  text-align: center;
  color: #1a237e;
  margin-bottom: 20px;
  font-size: 1.8rem;
  font-weight: normal;
}

.tournament-bracket-root {
  font-family: Arial, sans-serif;
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.container {
  display: flex;
  gap: 20px;
  align-items: flex-start;
  min-height: 500px;
}

.bracket-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.bracket-section {
  background: #fff;
  padding: 15px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.bracket-section h2 {
  color: #1a237e;
  font-size: 1.2rem;
  font-weight: 600;
  margin: 0 0 15px 0;
}

.bracket-container {
  position: relative;
  overflow: auto;
  background: #fff;
  padding: 15px;
  border-radius: 6px;
  border: 1px solid #ccc;
  box-shadow: 0 2px 12px rgba(0,0,0,0.08);
  min-height: 400px;
  display: flex;
  align-items: flex-start;
}

.zoom-controls {
  position: absolute;
  top: 20px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  z-index: 10;
  background: rgba(255, 255, 255, 0.9);
  border-radius: 6px;
  padding: 5px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}

.zoom-btn {
  width: 32px;
  height: 32px;
  background: #007acc;
  border: none;
  border-radius: 4px;
  color: white;
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
}

.zoom-btn:hover {
  background: #005a9e;
  transform: scale(1.05);
}

.zoom-btn:active {
  transform: scale(0.95);
}

.interaction-hint {
  position: absolute;
  bottom: 15px;
  left: 15px;
  background: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 8px 12px;
  border-radius: 4px;
  font-size: 12px;
  z-index: 5;
  user-select: none;
  opacity: 0.8;
  transition: opacity 0.3s ease;
}

.interaction-hint:hover {
  opacity: 1;
}

.d3-bracket {
  width: 100%;
  height: 100%;
  display: block;
  cursor: grab;
}

.d3-bracket:active {
  cursor: grabbing;
}

.final-match-container {
  display: flex;
  justify-content: center;
  margin: 20px 0;
}

.final-match {
  background: #f9f9f9;
  border: 2px solid #007acc;
  border-radius: 8px;
  padding: 20px;
  min-width: 300px;
}

.participant {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid #eee;
}

.participant:last-of-type {
  border-bottom: none;
}

.participant.winner {
  font-weight: bold;
  color: #2a7f2a;
  background-color: #e8f5e8;
  border-radius: 4px;
  padding: 10px;
}

.participant span {
  flex: 1;
  margin-right: 10px;
}

input[type="number"] {
  width: 60px;
  text-align: center;
  padding: 5px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

button {
  background: #409EFF;
  color: white;
  border: none;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  margin-top: 10px;
  width: 100%;
  font-size: 14px;
  font-weight: bold;
  transition: all 0.3s;
}

button:disabled {
  background: #cccccc;
  cursor: not-allowed;
}

button:hover:not(:disabled) {
  background: #66b1ff;
}


</style>