<template>
    <div class="tournaments-view">
        <div class="tournament-config">
            <h3>{{ $t('tournaments.date') }}</h3>
            <select v-model="selectedDate">
                <option value="new">{{ $t('tournaments.newTournament') }}</option>
                <option v-for="date in dates" :key="date.id" :value="date.id">
                    <template v-if="date.name">
                        {{ date.name }} ({{ date.date ? new Date(date.date).toLocaleDateString('de-DE', {
                            year: 'numeric',
                            month: '2-digit',
                            day: '2-digit'
                        }) : $t('tournaments.unknownDate') }})
                    </template>
                    <template v-else>
                        {{ date.date ? new Date(date.date).toLocaleDateString('de-DE', {
                            year: 'numeric',
                            month: '2-digit',
                            day: '2-digit'
                        }) : 'Unbekanntes Datum' }}
                    </template>
                </option>
            </select>
            <div v-if="selectedDate === 'new'" class="new-tournament">
                <label>
                    {{ $t('tournaments.name') }}:
                    <input type="text" v-model="newTournamentName" :placeholder="$t('tournaments.tournamentName')" />
                </label>
                <label>
                    {{ $t('tournaments.date') }}:
                    <input type="date" v-model="newDate" />
                </label>
                <label>
                    {{ $t('tournaments.winningSets') }}:
                    <input type="number" v-model.number="newWinningSets" min="1" />
                </label>
                <button @click="createTournament">{{ $t('tournaments.create') }}</button>
            </div>
        </div>
        <div v-if="selectedDate !== 'new'" class="tournament-setup">
            <!-- Tab-Navigation -->
            <div class="tournament-tabs">
                <button 
                    @click="setActiveTab('config')" 
                    :class="['tab-button', { 'active': activeTab === 'config' }]">
                    {{ $t('tournaments.tabConfig') }}
                </button>
                <button 
                    @click="setActiveTab('participants')" 
                    :class="['tab-button', { 'active': activeTab === 'participants' }]">
                    {{ $t('tournaments.tabParticipants') }}
                </button>
                <button 
                    @click="setActiveTab('groups')" 
                    :class="['tab-button', { 'active': activeTab === 'groups' }]"
                    :disabled="!isGroupTournament">
                    {{ $t('tournaments.tabGroups') }}
                </button>
                <button 
                    @click="setActiveTab('results')" 
                    :class="['tab-button', { 'active': activeTab === 'results' }]">
                    {{ $t('tournaments.tabResults') }}
                </button>
                <button 
                    @click="setActiveTab('placements')" 
                    :class="['tab-button', { 'active': activeTab === 'placements' }]">
                    {{ $t('tournaments.tabPlacements') }}
                </button>
            </div>

            <!-- Tab: Konfiguration -->
            <TournamentConfigTab
                v-if="activeTab === 'config'"
                :club-id="currentClub"
                :tournament-id="selectedDate"
                :tournament-name="currentTournamentName"
                :tournament-date="currentTournamentDate"
                :winning-sets="currentWinningSets"
                :number-of-tables="currentNumberOfTables"
                :is-group-tournament="isGroupTournament"
                :tournament-classes="tournamentClasses"
                :show-classes="showClasses"
                :editing-class-id="editingClassId"
                :editing-class-name="editingClassName"
                :editing-class-is-doubles="editingClassIsDoubles"
                :editing-class-gender="editingClassGender"
                :editing-class-max-birth-year="editingClassMaxBirthYear"
                :new-class-name="newClassName"
                :new-class-is-doubles="newClassIsDoubles"
                :new-class-gender="newClassGender"
                :new-class-max-birth-year="newClassMaxBirthYear"
                @update:tournamentName="currentTournamentName = $event; updateTournament()"
                @update:tournamentDate="currentTournamentDate = $event; updateTournament()"
                @update:winningSets="currentWinningSets = $event; updateTournament()"
                @update:numberOfTables="currentNumberOfTables = $event; updateTournament()"
                @update:isGroupTournament="isGroupTournament = $event; onModusChange()"
                @generate-pdf="generatePDF"
                @edit-class="editClass"
                @save-class-edit="saveClassEdit"
                @cancel-class-edit="cancelClassEdit"
                @delete-class="deleteClass"
                @add-class="addClass"
                @add-class-error="handleAddClassError"
                @handle-class-input-blur="handleClassInputBlur"
                @update:editingClassName="editingClassName = $event"
                @update:editingClassIsDoubles="editingClassIsDoubles = $event"
                @update:editingClassGender="editingClassGender = $event"
                @update:editingClassMaxBirthYear="editingClassMaxBirthYear = $event"
                @update:newClassName="newClassName = $event"
                @update:newClassIsDoubles="newClassIsDoubles = $event"
                @update:newClassGender="newClassGender = $event"
                @update:newClassMaxBirthYear="newClassMaxBirthYear = $event"
            />

            <!-- Tab: Teilnehmer -->
            <TournamentParticipantsTab
                v-if="activeTab === 'participants'"
                :selected-date="selectedDate"
                :selected-view-class="selectedViewClass"
                :tournament-classes="tournamentClasses"
                :show-participants="showParticipants"
                :show-pairings="showPairings"
                :can-assign-class="canAssignClass"
                :allows-external="allowsExternal"
                :is-group-tournament="isGroupTournament"
                :selected-member="selectedMember"
                :club-members="clubMembers"
                :has-training-today="hasTrainingToday"
                :new-external-participant="newExternalParticipant"
                :participants="participants"
                :external-participants="externalParticipants"
                :groups="groups"
                :pairings="pairings"
                :new-pairing="newPairing"
                @update:selectedViewClass="selectedViewClass = $event"
                @update:selectedMember="selectedMember = $event"
                @add-participant="addParticipant()"
                @load-participants-from-training="loadParticipantsFromTraining()"
                @update:newExternalParticipant="newExternalParticipant = $event"
                @add-external-participant="addExternalParticipant()"
                @update-participant-seeded="updateParticipantSeeded"
                @update-participant-group="updateParticipantGroup"
                @update-participant-class="updateParticipantClass"
                @remove-participant="removeParticipant"
                @toggle-pairings="togglePairings()"
                @update:newPairing="newPairing = $event"
                @add-pairing="addPairing()"
                @create-random-pairings="createRandomPairings()"
                @update-pairing-seeded="updatePairingSeeded"
                @remove-pairing="removePairing"
            />

            <!-- Tab: Gruppen -->
            <TournamentGroupsTab
                v-if="activeTab === 'groups'"
                :selected-date="selectedDate"
                :selected-view-class="selectedViewClass"
                :tournament-classes="tournamentClasses"
                :is-group-tournament="isGroupTournament"
                :advancing-per-group="advancingPerGroup"
                :max-group-size="maxGroupSize"
                :groups-per-class-input="groupsPerClassInput"
                :number-of-groups="numberOfGroups"
                :groups="groups"
                :groups-by-class="groupsByClass"
                :group-rankings="groupRankings"
                :active-group-cells="activeGroupCells"
                :matches="matches"
                @update:selectedViewClass="selectedViewClass = $event"
                @update:advancingPerGroup="advancingPerGroup = $event; onModusChange()"
                @update:maxGroupSize="maxGroupSize = $event"
                @update:groupsPerClassInput="groupsPerClassInput = $event"
                @update:numberOfGroups="numberOfGroups = $event"
                @modus-change="onModusChange()"
                @group-count-change="onGroupCountChange()"
                @create-groups="createGroups()"
                @randomize-groups="randomizeGroups()"
                @reset-groups="resetGroups()"
                @reset-matches="resetMatches()"
                @create-matches="createMatches()"
                @highlight-match="highlightMatch"
            />

            <!-- Tab: Ergebnisse -->
                <TournamentResultsTab
                v-if="activeTab === 'results'"
                :selected-date="selectedDate"
                :club-id="currentClub"
                :number-of-tables="currentNumberOfTables"
                :selected-view-class="selectedViewClass"
                :tournament-classes="tournamentClasses"
                :group-matches="groupMatches"
                :knockout-matches="knockoutMatches"
                :active-match-id="activeMatchId"
                :editing-result="editingResult"
                :can-start-knockout="canStartKnockout"
                :show-knockout="showKnockout"
                :can-reset-knockout="canResetKnockout"
                :get-total-number-of-groups="getTotalNumberOfGroups"
                :grouped-ranking-list="groupedRankingList"
                :participants="participants"
                :groups="groups"
                :pairings="pairings"
                @update:selectedViewClass="selectedViewClass = $event"
                @update:activeMatchId="activeMatchId = $event"
                @update:editingResult="editingResult = $event"
                @save-edited-result="saveEditedResult"
                @cancel-edit="cancelEdit"
                @start-edit-result="startEditResult"
                @save-match-result="saveMatchResult"
                @delete-set="deleteMatchSet"
                @finish-match="finishMatch"
                @reopen-match="reopenMatch"
                @set-match-active="setMatchActive"
                @start-matches="startMatches"
                @start-knockout="startKnockout"
                @reset-knockout="resetKnockout"
                @tables-distributed="handleTablesDistributed"
            />

            <!-- Tab: Platzierungen -->
            <TournamentPlacementsTab
                v-if="activeTab === 'placements'"
                :selected-date="selectedDate"
                :selected-view-class="selectedViewClass"
                :tournament-classes="tournamentClasses"
                :knockout-matches="knockoutMatches"
                :groups="groups"
                :group-rankings="groupRankings"
                :grouped-ranking-list="groupedRankingList"
                :participants="participants"
                :external-participants="externalParticipants"
                :pairings="pairings"
                :club-id="currentClub"
                @update:selectedViewClass="selectedViewClass = $event"
            />
        </div>
    </div>

    <!-- Info Dialog -->
    <InfoDialog
        v-model="infoDialog.isOpen"
        :title="infoDialog.title"
        :message="infoDialog.message"
        :details="infoDialog.details"
        :detailsHtml="infoDialog.detailsHtml"
        :type="infoDialog.type"
    />
    
    <!-- Confirm Dialog -->
    <ConfirmDialog
        v-model="confirmDialog.isOpen"
        :title="confirmDialog.title"
        :message="confirmDialog.message"
        :details="confirmDialog.details"
        :type="confirmDialog.type"
        @confirm="handleConfirmResult(true)"
        @cancel="handleConfirmResult(false)"
    />
</template>

<script>
import { mapGetters } from 'vuex';
import apiClient from '../apiClient.js';
import InfoDialog from '../components/InfoDialog.vue';
import ConfirmDialog from '../components/ConfirmDialog.vue';
import { buildInfoConfig, buildConfirmConfig, safeErrorMessage } from '../utils/dialogUtils.js';
import { connectSocket, disconnectSocket, onTournamentChanged, offTournamentChanged } from '../services/socketService.js';
import PDFGenerator from '../components/PDFGenerator.js';
import TournamentConfigTab from '../components/tournament/TournamentConfigTab.vue';
import TournamentGroupsTab from '../components/tournament/TournamentGroupsTab.vue';
import TournamentParticipantsTab from '../components/tournament/TournamentParticipantsTab.vue';
import TournamentResultsTab from '../components/tournament/TournamentResultsTab.vue';
import TournamentPlacementsTab from '../components/tournament/TournamentPlacementsTab.vue';
export default {
    name: 'TournamentTab',
    components: {
        InfoDialog,
        ConfirmDialog,
        TournamentConfigTab,
        TournamentGroupsTab,
        TournamentParticipantsTab,
        TournamentResultsTab,
        TournamentPlacementsTab
    },
    props: {
        allowsExternal: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            // Dialog States
            infoDialog: {
                isOpen: false,
                title: '',
                message: '',
                details: '',
                type: 'info'
            },
            confirmDialog: {
                isOpen: false,
                title: '',
                message: '',
                details: '',
                type: 'info',
                resolveCallback: null
            },
            selectedDate: 'new',
            activeTab: 'config', // Aktiver Tab: 'config', 'groups', 'participants', 'results'
            newDate: '',
            newTournamentName: '',
            newWinningSets: 3,
            currentTournamentName: '',
            currentTournamentDate: '',
            currentWinningSets: 3,
            dates: [],
            participants: [],
            selectedMember: null,
            selectedViewClass: null, // Ausgewählte Klasse für die Anzeige (null = alle)
            clubMembers: [],
            advancingPerGroup: 1,
            numberOfGroups: 1,
            groupsPerClass: {}, // { classId: numberOfGroups }
            currentNumberOfTables: null,
            maxGroupSize: null,
            isGroupTournament: false,
            groups: [],
            matches: [],
            showKnockout: false,
            showParticipants: false, // Kollaps-Status für Teilnehmerliste
            hasTrainingToday: false, // Gibt es einen Trainingstag heute?
            editingResult: {
                matchId: null,   // aktuell bearbeitetes Match
                set: null,   // aktuell bearbeitete Satz‑Nummer
                value: ''      // Eingabewert
            },
            activeMatchId: null,  // Angeklicktes/aktives Match (für Hervorhebung)
            tournamentClasses: [], // Turnierklassen
            newClassName: '', // Name für neue Klasse
            newClassIsDoubles: false, // Ist die neue Klasse ein Doppel?
            newClassGender: null, // Geschlecht für neue Klasse (null, 'male', 'female', 'mixed')
            newClassMaxBirthYear: null, // Maximales Geburtsjahr für neue Klasse
            showClasses: false, // Kollaps-Status für Klassen-Verwaltung
            editingClassId: null, // ID der gerade bearbeiteten Klasse
            editingClassName: '', // Temporärer Name während der Bearbeitung
            editingClassIsDoubles: false, // Temporäres isDoubles während der Bearbeitung
            editingClassGender: null, // Temporäres gender während der Bearbeitung
            editingClassMaxBirthYear: null, // Temporäres minBirthYear während der Bearbeitung
            externalParticipants: [], // Externe Teilnehmer (nur bei allowsExternal = true)
            newExternalParticipant: { // Neuer externer Teilnehmer (nur bei allowsExternal = true)
                firstName: '',
                lastName: '',
                club: '',
                birthDate: null,
                gender: 'unknown'
            },
            showPairings: false, // Kollaps-Status für Paarungen
            pairings: [], // Doppel-Paarungen
            newPairing: { // Neue Paarung
                player1Id: null,
                player2Id: null,
                seeded: false
            }
        };
    },
    computed: {
        ...mapGetters(['isAuthenticated', 'currentClub']),

        knockoutMatches() {
            const koMatches = this.matches.filter(m => m.round !== 'group' && !this.isEmptyThirdPlacePlaceholder(m));
            // Sortiere nach Klasse, dann nach Runde
            return koMatches.sort((a, b) => {
                // Zuerst nach Klasse (null zuletzt)
                const aClass = a.classId || 999999;
                const bClass = b.classId || 999999;
                if (aClass !== bClass) return aClass - bClass;
                
                // Dann nach Runde (frühere Runden zuerst: Achtelfinale, Viertelfinale, Halbfinale, Finale)
                // Extrahiere die Rundennamen (ohne Zahlen in Klammern)
                const getRoundType = (roundName) => {
                    if (roundName.includes('Achtelfinale')) return 0;
                    if (roundName.includes('Viertelfinale')) return 1;
                    if (roundName.includes('Halbfinale')) return 2;
                    if (roundName.includes('Spiel um Platz 3')) return 2.5;
                    if (roundName.includes('Finale')) return 3;
                    // Für Runden wie "6-Runde", "8-Runde" etc. - extrahiere die Zahl
                    const numberMatch = roundName.match(/(\d+)-Runde/);
                    if (numberMatch) {
                        const num = parseInt(numberMatch[1]);
                        // Größere Zahlen = frühere Runden, also umgekehrt sortieren
                        return -num; // Negativ, damit größere Zahlen zuerst kommen
                    }
                    return 999; // Unbekannte Runden zuletzt
                };
                
                const aRoundType = getRoundType(a.round);
                const bRoundType = getRoundType(b.round);
                if (aRoundType !== bRoundType) {
                    return aRoundType - bRoundType;
                }
                
                // Wenn gleicher Typ, alphabetisch sortieren
                return a.round.localeCompare(b.round);
            });
        },

        isEmptyThirdPlacePlaceholder(match) {
            return match
                && String(match.round || '').includes('Spiel um Platz 3')
                && !match.player1Id
                && !match.player2Id
                && !match.player1
                && !match.player2
                && !match.result;
        },

        // Computed property für aktive Gruppentabellen-Zellen
        activeGroupCells() {
            if (!this.activeMatchId) return [];
            
            const match = this.matches.find(m => m.id === this.activeMatchId);
            if (!match || match.round !== 'group') return [];
            
            return [
                `match-${match.player1.id}-${match.player2.id}`,
                `match-${match.player2.id}-${match.player1.id}`
            ];
        },

        groupMatches() {
            const filtered = this.matches.filter(m => m.round === 'group');
            
            // Gruppiere nach Runde
            const byRound = {};
            filtered.forEach(m => {
                const round = m.groupRound || 0;
                if (!byRound[round]) {
                    byRound[round] = [];
                }
                byRound[round].push(m);
            });
            
            // Sortiere die Runden
            const sortedRounds = Object.keys(byRound).sort((a, b) => Number(a) - Number(b));
            
            // Für jede Runde: Sortiere nach Gruppe, dann interleaved zusammenführen
            const result = [];
            sortedRounds.forEach(round => {
                const matchesInRound = byRound[round];
                
                // Gruppiere nach groupNumber
                const byGroup = {};
                matchesInRound.forEach(m => {
                    const groupNum = m.groupNumber || 0;
                    if (!byGroup[groupNum]) {
                        byGroup[groupNum] = [];
                    }
                    byGroup[groupNum].push(m);
                });
                
                // Sortiere die Gruppen
                const sortedGroups = Object.keys(byGroup).sort((a, b) => Number(a) - Number(b));
                
                // Interleaved: Nimm abwechselnd aus jeder Gruppe
                let maxLength = 0;
                sortedGroups.forEach(groupNum => {
                    maxLength = Math.max(maxLength, byGroup[groupNum].length);
                });
                
                for (let i = 0; i < maxLength; i++) {
                    sortedGroups.forEach(groupNum => {
                        if (i < byGroup[groupNum].length) {
                            result.push(byGroup[groupNum][i]);
                        }
                    });
                }
            });

            // Beendete Spiele nach unten verschieben (unfertige zuerst)
            result.sort((a, b) => {
                const aFinished = a.isFinished ? 1 : 0;
                const bFinished = b.isFinished ? 1 : 0;
                if (aFinished !== bFinished) return aFinished - bFinished;
                return 0;
            });

            return result;
        },

        groupsByClass() {
            const grouped = {};
            this.groups.forEach(g => {
                const classKey = g.classId != null ? String(g.classId) : 'null';
                if (!grouped[classKey]) {
                    grouped[classKey] = [];
                }
                grouped[classKey].push(g);
            });
            return grouped;
        },

        groupRankings() {
            // Die Teilnehmer kommen bereits sortiert vom Backend mit allen benötigten Statistiken
            const rankings = {};
            this.groups.forEach(g => {
                rankings[g.groupId] = g.participants.map(p => ({
                    id: p.id,
                    name: p.name,
                    seeded: p.seeded || false,
                    position: p.position || 0,
                    points: p.points || 0,
                    setsWon: p.setsWon || 0,
                    setsLost: p.setsLost || 0,
                    setDiff: p.setDiff || 0,
                    pointsWon: Math.abs(p.pointsWon || 0),
                    pointsLost: Math.abs(p.pointsLost || 0),
                    pointRatio: p.pointRatio || 0,
                    matchesWon: p.matchesWon || 0,
                    matchesLost: p.matchesLost || 0,
                    isExternal: p.isExternal || false
                }));
            });
            return rankings;
        },

        // Mapping von groupId zu groupNumber für die Teilnehmer-Auswahl
        groupIdToNumberMap() {
            const map = {};
            this.groups.forEach(g => {
                map[g.groupId] = g.groupNumber;
            });
            return map;
        },

        groupsPerClassInput: {
            get() {
                if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
                    return 0;
                }
                if (this.selectedViewClass === '__none__') {
                    return this.groupsPerClass['null'] || 0;
                }
                const classId = Number(this.selectedViewClass);
                return this.groupsPerClass[classId] || 0;
            },
            set(value) {
                if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
                    return;
                }
                if (this.selectedViewClass === '__none__') {
                    this.groupsPerClass['null'] = value;
                } else {
                    const classId = Number(this.selectedViewClass);
                    this.groupsPerClass[classId] = value;
                }
            }
        },

        activeAssignmentClassId() {
            if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
                return undefined;
            }
            if (this.selectedViewClass === '__none__' || this.selectedViewClass === 'null') {
                return null;
            }
            const id = Number(this.selectedViewClass);
            return Number.isNaN(id) ? undefined : id;
        },

        activeAssignmentClassLabel() {
            if (this.activeAssignmentClassId === undefined) {
                return this.$t('tournaments.selectClassPrompt');
            }
            if (this.activeAssignmentClassId === null) {
                return this.$t('tournaments.withoutClass');
            }
            return this.getClassName(this.activeAssignmentClassId) || this.$t('tournaments.unknown');
        },

        canAssignClass() {
            return this.activeAssignmentClassId !== undefined;
        },

        rankingList() {
            const finalMatch = this.knockoutMatches.find(
                m => m.round.toLowerCase() === 'finale'
            );
            if (!finalMatch || !finalMatch.isFinished) return [];
            const list = [];
            
            // Gruppiere nach Klassen
            const matchesByClass = {};
            this.knockoutMatches.forEach(m => {
                const classKey = m.classId != null ? String(m.classId) : 'null';
                if (!matchesByClass[classKey]) {
                    matchesByClass[classKey] = [];
                }
                matchesByClass[classKey].push(m);
            });
            
            // Verarbeite jede Klasse separat
            Object.entries(matchesByClass).forEach(([classKey, classMatches]) => {
                const classFinalMatch = classMatches.find(m => m.round.toLowerCase() === 'finale');
                if (!classFinalMatch || !classFinalMatch.isFinished) return;
                
                const [s1, s2] = classFinalMatch.result.split(':').map(n => +n);
                const winner = s1 > s2 ? classFinalMatch.player1 : classFinalMatch.player2;
                const loser = s1 > s2 ? classFinalMatch.player2 : classFinalMatch.player1;
                list.push({ position: 1, member: winner.member, classId: classFinalMatch.classId });
                list.push({ position: 2, member: loser.member, classId: classFinalMatch.classId });
                
                const roundsMap = {};
                classMatches.forEach(m => {
                    if (m.round.toLowerCase() === 'finale') return;
                    (roundsMap[m.round] ||= []).push(m);
                });
                Object.values(roundsMap).forEach(matches => {
                    const M = matches.length;
                    const pos = M + 1;
                    matches.forEach(match => {
                        const [a, b] = match.result.split(':').map(n => +n);
                        const knockedOut = a > b ? match.player2 : match.player1;
                        list.push({ position: pos, member: knockedOut.member, classId: match.classId });
                    });
                });
            });
            
            return list.sort((a, b) => {
                // Zuerst nach Klasse, dann nach Position
                const aClass = a.classId || 999999;
                const bClass = b.classId || 999999;
                if (aClass !== bClass) return aClass - bClass;
                return a.position - b.position;
            });
        },
        
        // Computed property für gruppiertes Ranking (nach Klassen)
        groupedRankingList() {
            const ranking = this.knockoutMatches.length > 0 ? this.extendedRankingList : this.rankingList;
            if (ranking.length === 0) return {};
            
            const grouped = {};
            ranking.forEach(entry => {
                const classKey = entry.classId != null ? String(entry.classId) : 'null';
                if (!grouped[classKey]) {
                    grouped[classKey] = [];
                }
                grouped[classKey].push(entry);
            });
            
            return grouped;
        },

        // Erweiterte Ranking-Liste, die auch teilweise abgeschlossene K.O.-Runden berücksichtigt
        // WICHTIG: Diese Liste ist nicht nach Klassen gruppiert - das wird im PDF gemacht
        extendedRankingList() {
            if (this.knockoutMatches.length === 0) return [];
            
            const list = [];
            const finishedMatches = this.knockoutMatches.filter(m => m.isFinished);
            
            if (finishedMatches.length === 0) return [];
            
            // Finde alle Spieler, die noch im Turnier sind (Gewinner von abgeschlossenen Matches, die noch nicht ausgeschieden sind)
            const stillInTournament = new Set();
            finishedMatches.forEach(match => {
                // BYE oder regulär robust auswerten
                let winner = null;
                if (match.result === 'BYE') {
                    winner = match.player1 || match.player2 || null;
                } else if (typeof match.result === 'string' && match.result.includes(':')) {
                    const [sa, sb] = match.result.split(':').map(n => Number(n));
                    if (Number.isFinite(sa) && Number.isFinite(sb)) {
                        winner = sa > sb ? match.player1 : match.player2;
                    }
                }
                if (!winner) return;
                const winnerId = winner.member ? winner.member.id : winner.id;
                
                // Prüfe, ob der Gewinner noch in einem nicht abgeschlossenen Match ist
                const hasUnfinishedMatch = this.knockoutMatches.some(m => 
                    !m.isFinished && 
                    m.classId === match.classId && // WICHTIG: Nur Matches derselben Klasse prüfen
                    ((m.player1 && m.player1.member && m.player1.member.id === winnerId) ||
                        (m.player1 && m.player1.id === winnerId) ||
                        (m.player2 && m.player2.member && m.player2.member.id === winnerId) ||
                        (m.player2 && m.player2.id === winnerId))
                );
                
                if (hasUnfinishedMatch) {
                    stillInTournament.add(`${match.classId || 'null'}-${winnerId}`);
                }
            });
            
            // Gruppiere nach Klasse, dann nach Runden
            const matchesByClass = {};
            finishedMatches.forEach(m => {
                const classKey = m.classId != null ? String(m.classId) : 'null';
                if (!matchesByClass[classKey]) {
                    matchesByClass[classKey] = [];
                }
                matchesByClass[classKey].push(m);
            });
            
            // Verarbeite jede Klasse separat
            Object.entries(matchesByClass).forEach(([classKey, classMatches]) => {
                const hasThirdPlace = classMatches.some(m => (m.round || '').toLowerCase().includes('platz 3'));
                // Gruppiere nach Runden innerhalb dieser Klasse
                const roundsMap = {};
                classMatches.forEach(m => {
                    const roundName = m.round.toLowerCase();
                    (roundsMap[roundName] ||= []).push(m);
                });
                
                // Sortiere Runden: Finale zuerst, dann nach Anzahl der Matches (mehr = früher)
                const roundOrder = ['finale', 'halbfinale', 'viertelfinale', 'achtelfinale'];
                const sortedRounds = Object.entries(roundsMap).sort((a, b) => {
                    const aIndex = roundOrder.indexOf(a[0]);
                    const bIndex = roundOrder.indexOf(b[0]);
                    if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex;
                    if (aIndex !== -1) return -1;
                    if (bIndex !== -1) return 1;
                    // Für andere Runden: sortiere nach Anzahl der Matches (mehr = früher, da später ausgeschieden)
                    return b[1].length - a[1].length;
                });
                
                // Hilfsfunktion: Positionszahl für KO-Runden nach Anzahl Matches
                const positionForRound = (roundName, matchesCount) => {
                    const rn = (roundName || '').toLowerCase();
                    if (rn === 'finale') return 1; // wird separat behandelt
                    if (rn.includes('halbfinale')) return 3; // nur wenn KEIN Platz-3-Spiel existiert
                    // Viertelfinale: 4 Matches -> Plätze 5..8
                    if (rn.includes('viertelfinale')) return 5;
                    // Achtelfinale: 8 Matches -> Plätze 9..16
                    if (rn.includes('achtelfinale')) return 9;
                    // Generisch: matchesCount + 1
                    return Number(matchesCount) + 1;
                };
                
                sortedRounds.forEach(([roundName, matches]) => {
                    if (roundName === 'finale') {
                        // Finale: 1. und 2. Platz
                        const match = matches[0];
                        let winner = null;
                        let loser = null;
                        if (match.result === 'BYE') {
                            winner = match.player1 || match.player2 || null;
                            loser = winner === match.player1 ? match.player2 : match.player1;
                        } else if (typeof match.result === 'string' && match.result.includes(':')) {
                            const [s1, s2] = match.result.split(':').map(n => Number(n));
                            if (Number.isFinite(s1) && Number.isFinite(s2)) {
                                winner = s1 > s2 ? match.player1 : match.player2;
                                loser = s1 > s2 ? match.player2 : match.player1;
                            }
                        }
                        if (!winner || !loser) return;
                        const winnerId = winner.member ? winner.member.id : winner.id;
                        const loserId = loser.member ? loser.member.id : loser.id;
                        
                        // Nur hinzufügen, wenn nicht mehr im Turnier
                        const toMember = (p) => p?.member ? p.member : (p ? { id: p.id, firstName: p.firstName, lastName: p.lastName } : null);
                        if (!stillInTournament.has(`${classKey}-${winnerId}`)) {
                            const mLike = toMember(winner);
                            if (mLike) list.push({ position: 1, member: mLike, classId: match.classId });
                        }
                        if (!stillInTournament.has(`${classKey}-${loserId}`)) {
                            const mLike = toMember(loser);
                            if (mLike) list.push({ position: 2, member: mLike, classId: match.classId });
                        }
                        // Finale setzt 1/2, keine globale Positionsvariable nötig
                    } else {
                        // Überspringe Platz-3-Runde hier; sie wird separat als 3/4 bewertet
                        if ((roundName || '').toLowerCase().includes('platz 3')) {
                            return;
                        }
                        // Andere Runden: Alle Verlierer bekommen die gleiche Position
                        const numMatches = matches.length;
                        let position = positionForRound(roundName, numMatches);
                        matches.forEach(match => {
                            let knockedOut = null;
                            if (match.result === 'BYE') {
                                // Der fehlende Spieler gilt als ausgeschieden
                                knockedOut = (!match.player1) ? match.player1 : (!match.player2 ? match.player2 : null);
                                // Falls beide vorhanden (sollte nicht BYE sein), fall back
                                if (!knockedOut) {
                                    const [a, b] = (match.result || '').split(':').map(n => Number(n));
                                    if (Number.isFinite(a) && Number.isFinite(b)) {
                                        knockedOut = a > b ? match.player2 : match.player1;
                                    }
                                }
                            } else {
                                const [a, b] = (match.result || '').split(':').map(n => Number(n));
                                if (Number.isFinite(a) && Number.isFinite(b)) {
                                    knockedOut = a > b ? match.player2 : match.player1;
                                }
                            }
                            if (!knockedOut) return;
                            const knockedOutId = knockedOut.member ? knockedOut.member.id : knockedOut.id;
                            
                            // Wenn ein Platz-3-Spiel existiert, füge Halbfinal-Verlierer hier NICHT als Platz 3 hinzu
                            const isSemi = (match.round || '').toLowerCase().includes('halbfinale');
                            if (hasThirdPlace && isSemi) return;

                            // Nur hinzufügen, wenn nicht mehr im Turnier
                            if (!stillInTournament.has(`${classKey}-${knockedOutId}`)) {
                                const mLike = knockedOut.member ? knockedOut.member : { id: knockedOut.id, firstName: knockedOut.firstName, lastName: knockedOut.lastName };
                                if (mLike) list.push({ position: position, member: mLike, classId: match.classId });
                            }
                        });
                        // Bei Viertelfinale/Achtelfinale erhalten Verlierer die korrekten Startpositionen (5 bzw. 9)
                    }
                });

                // Platz-3-Spiel explizit werten (3/4)
                const thirdMatch = classMatches.find(m => (m.round || '').toLowerCase().includes('platz 3'));
                if (thirdMatch && thirdMatch.isFinished) {
                    let winner = null;
                    let loser = null;
                    if (thirdMatch.result === 'BYE') {
                        winner = thirdMatch.player1 || thirdMatch.player2 || null;
                        loser = winner === thirdMatch.player1 ? thirdMatch.player2 : thirdMatch.player1;
                    } else if (typeof thirdMatch.result === 'string' && thirdMatch.result.includes(':')) {
                        const [s1, s2] = thirdMatch.result.split(':').map(n => Number(n));
                        if (Number.isFinite(s1) && Number.isFinite(s2)) {
                            winner = s1 > s2 ? thirdMatch.player1 : thirdMatch.player2;
                            loser = s1 > s2 ? thirdMatch.player2 : thirdMatch.player1;
                        }
                    }
                    const toMember = (p) => p?.member ? p.member : (p ? { id: p.id, firstName: p.firstName, lastName: p.lastName } : null);
                    if (winner) {
                        const mLike = toMember(winner);
                        if (mLike) list.push({ position: 3, member: mLike, classId: thirdMatch.classId });
                    }
                    if (loser) {
                        const mLike = toMember(loser);
                        if (mLike) list.push({ position: 4, member: mLike, classId: thirdMatch.classId });
                    }
                }
            });

            // Dedupliziere Einträge pro Klasse/Spieler und behalte die beste (niedrigste) Position
            const dedup = new Map();
            const out = [];
            for (const entry of list) {
                const key = `${entry.classId || 'null'}:${entry.member?.id ?? (entry.member?.firstName || '') + '|' + (entry.member?.lastName || '')}`;
                const existing = dedup.get(key);
                if (!existing || Number(entry.position) < Number(existing.position)) {
                    dedup.set(key, entry);
                }
            }
            for (const v of dedup.values()) out.push(v);
            return out.sort((a, b) => {
                const ac = a.classId || 999999;
                const bc = b.classId || 999999;
                if (ac !== bc) return ac - bc;
                return Number(a.position) - Number(b.position);
            });
            
            return list.sort((a, b) => {
                // Zuerst nach Klasse, dann nach Position
                const aClass = a.classId || 999999;
                const bClass = b.classId || 999999;
                if (aClass !== bClass) return aClass - bClass;
                return a.position - b.position;
            });
        },

        canStartKnockout() {
            const totalParticipants = this.allowsExternal
                ? this.participants.length + this.externalParticipants.length
                : this.participants.length;
            if (totalParticipants < 2) return false;
            if (!this.isGroupTournament) {
                // kein Gruppenmodus → immer starten
                return true;
            }
            if ((this.groups || []).some(g => g.stageId !== null && g.stageId !== undefined)) {
                return false;
            }
            // Gruppenmodus → prüfe Anzahl der Gruppen
            const totalGroups = this.getTotalNumberOfGroups;
            if (totalGroups <= 1) {
                // Nur eine Gruppe → keine K.O.-Runde möglich
                return false;
            }
            // Gruppenmodus → nur, wenn es Gruppenspiele gibt und alle beendet sind
            return this.groupMatches.length > 0
                && this.groupMatches.every(m => m.isFinished);
        },
        
        getTotalNumberOfGroups() {
            if (!this.isGroupTournament) return 0;
            // Verwende die tatsächliche Anzahl der erstellten Gruppen
            if (this.groups && this.groups.length > 0) {
                return this.groups.length;
            }
            // Fallback: Wenn es Klassen gibt, summiere die Gruppen pro Klasse
            if (this.tournamentClasses && this.tournamentClasses.length > 0) {
                return Object.values(this.groupsPerClass).reduce((sum, count) => sum + (count || 0), 0);
            }
            // Sonst verwende numberOfGroups
            return this.numberOfGroups || 0;
        },

        canResetKnockout() {
            return this.knockoutMatches.length > 0
                || (this.groups || []).some(g => g.stageId !== null && g.stageId !== undefined);
        },
    },
    watch: {
        selectedDate: {
            immediate: true,
            handler: async function (val) {
                if (val === 'new') {
                    // Leere die Matches-Liste, wenn "neues Turnier" ausgewählt wird
                    this.matches = [];
                    this.groups = [];
                    this.showKnockout = false;
                    this.showParticipants = true;
                    this.selectedViewClass = '__none__';
                    this.activeTab = 'config'; // Setze Tab zurück auf Konfiguration
                    return;
                }
                this.selectedViewClass = null;
                this.activeTab = 'config'; // Setze Tab zurück auf Konfiguration beim Laden
                await this.loadTournamentData();
            }
        },
        selectedViewClass: {
            handler: async function (newVal) {
                if (newVal !== null && newVal !== undefined && newVal !== '__none__' && this.isClassDoubles(newVal)) {
                    await this.loadPairings();
                } else {
                    this.pairings = [];
                }
            },
            immediate: false
        }
    },
    async created() {
        if (!this.isAuthenticated) {
            this.$router.push('/login');
            return;
        }
        const d = await apiClient.get(`/tournament/${this.currentClub}`);
        // Filtere Turniere basierend auf allowsExternal Prop
        if (this.allowsExternal) {
            this.dates = d.data.filter(t => t.allowsExternal === true || t.allowsExternal === 1);
        } else {
            this.dates = d.data.filter(t => !t.allowsExternal || t.allowsExternal === false || t.allowsExternal === 0);
        }
        const m = await apiClient.get(
            `/clubmembers/get/${this.currentClub}/false`
        );
        // Sortiere alphabetisch: zuerst nach Vorname, dann nach Nachname
        const sorted = (m.data || []).sort((a, b) => {
            const firstNameA = (a.firstName || '').toLowerCase();
            const firstNameB = (b.firstName || '').toLowerCase();
            if (firstNameA !== firstNameB) {
                return firstNameA.localeCompare(firstNameB, 'de');
            }
            const lastNameA = (a.lastName || '').toLowerCase();
            const lastNameB = (b.lastName || '').toLowerCase();
            return lastNameA.localeCompare(lastNameB, 'de');
        });
        this.clubMembers = sorted;
    },
    mounted() {
        // Lade Turniere beim Start
        this.loadTournaments();
        
        // Socket.IO verbinden
        try {
            if (this.currentClub) {
                connectSocket(this.currentClub);
                onTournamentChanged(this.handleTournamentChanged);
            }
        } catch (error) {
            console.error('Fehler beim Verbinden mit Socket:', error);
        }
        
        // Event-Listener für das Entfernen des Highlights
        document.addEventListener('click', (e) => {
            // Entferne Highlight nur wenn nicht auf eine Matrix-Zelle geklickt wird
            if (!e.target.closest('.match-cell')) {
                this.clearHighlight();
            }
        });
        
        // Event-Listener für Eingabefelder
        document.addEventListener('input', () => {
            this.clearHighlight();
        });
    },
    beforeUnmount() {
        // Socket-Listener entfernen
        offTournamentChanged(this.handleTournamentChanged);
        disconnectSocket();
    },
    methods: {
        // Fallback-Kompatibilität: einige Aufrufe erwarten loadParticipants()
        async loadParticipants() {
            await this.loadTournamentData();
        },
        setActiveTab(tab) {
            this.activeTab = tab;
        },
        // Hilfsmethode: Hole alle Teilnehmer (intern + extern) - ohne Duplikate
        allParticipantsList() {
            const all = this.allowsExternal
                ? [
                    ...this.participants.map(p => ({ ...p, isExternal: false })),
                    ...this.externalParticipants.map(p => ({ ...p, isExternal: true }))
                ]
                : this.participants.map(p => ({ ...p, isExternal: false }));
            
            // Entferne Duplikate basierend auf ID
            const seen = new Set();
            return all.filter(p => {
                const key = p.id || `${p.clubMemberId || p.externalId || ''}_${p.classId || 'null'}`;
                if (seen.has(key)) {
                    return false;
                }
                seen.add(key);
                return true;
            });
        },
        
        // Hole Teilnehmer für eine bestimmte Klasse
        getParticipantsForClass(classId) {
            return this.allParticipantsList().filter(p => {
                if (classId === null || classId === '__none__') {
                    return p.classId === null || p.classId === undefined;
                }
                // Konvertiere beide Werte zu Zahlen für den Vergleich
                const classIdNum = typeof classId === 'string' ? parseInt(classId) : classId;
                const pClassIdNum = typeof p.classId === 'string' ? parseInt(p.classId) : p.classId;
                return pClassIdNum === classIdNum;
            }).sort((a, b) => {
                // Für interne Teilnehmer: verwende member.firstName/lastName
                // Für externe Teilnehmer: verwende firstName/lastName direkt
                const firstNameA = (a.member?.firstName || a.firstName || '').toLowerCase();
                const firstNameB = (b.member?.firstName || b.firstName || '').toLowerCase();
                if (firstNameA !== firstNameB) {
                    return firstNameA.localeCompare(firstNameB, 'de');
                }
                const lastNameA = (a.member?.lastName || a.lastName || '').toLowerCase();
                const lastNameB = (b.member?.lastName || b.lastName || '').toLowerCase();
                return lastNameA.localeCompare(lastNameB, 'de');
            });
        },
        
        // Hole Gruppen für eine bestimmte Klasse
        getGroupsForClass(classId) {
            return this.groups.filter(g => {
                if (classId === null) {
                    return g.classId === null || g.classId === undefined;
                }
                return g.classId === classId;
            });
        },
        
        // Prüfe, ob eine Klasse angezeigt werden soll
        shouldShowClass(classId) {
            // Wenn keine Klasse ausgewählt ist (null), zeige alle
            if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
                return true;
            }
            // Wenn "Ohne Klasse" ausgewählt ist
            if (this.selectedViewClass === '__none__' || this.selectedViewClass === 'null') {
                return classId === null;
            }
            // Vergleiche als Zahlen, um String/Number-Probleme zu vermeiden
            const selectedId = Number(this.selectedViewClass);
            const compareId = Number(classId);
            // Prüfe auf NaN (falls Parsing fehlschlägt)
            if (Number.isNaN(selectedId) || Number.isNaN(compareId)) {
                return false;
            }
            return selectedId === compareId;
        },
        getClassName(classId) {
            if (classId === null || classId === '__none__' || classId === 'null' || classId === 'undefined' || classId === undefined) {
                return this.$t('tournaments.withoutClass');
            }
            try {
                const classIdNum = typeof classId === 'string' ? parseInt(classId) : classId;
                const classItem = this.tournamentClasses.find(c => c.id === classIdNum);
                return classItem ? classItem.name : '';
            } catch (e) {
                return '';
            }
        },

        getGroupClassName(groupId) {
            if (!groupId) return '';
            const group = this.groups.find(g => g.groupId === groupId);
            if (!group || !group.classId) return '';
            return this.getClassName(group.classId);
        },

        getKnockoutMatchClassName(match) {
            if (!match) return '';
            
            // Verwende direkt die classId aus dem Match
            if (match.classId) {
                return this.getClassName(match.classId);
            }
            
            return '';
        },

        isClassDoubles(classId) {
            if (classId === null || classId === '__none__' || classId === 'null' || classId === undefined) {
                return false;
            }
            const classIdNum = typeof classId === 'string' ? parseInt(classId) : classId;
            const classItem = this.tournamentClasses.find(c => c.id === classIdNum);
            return classItem ? Boolean(classItem.isDoubles) : false;
        },

        // Dialog Helper Methods
        async showInfo(title, message, details = '', type = 'info') {
            this.infoDialog = buildInfoConfig({ title, message, details, type });
        },
        
        async showConfirm(title, message, details = '', type = 'info', options = {}) {
            return new Promise((resolve) => {
                this.confirmDialog = buildConfirmConfig({
                    title,
                    message,
                    details,
                    type,
                    resolveCallback: resolve,
                    ...options,
                });
            });
        },
        
        handleConfirmResult(confirmed) {
            if (this.confirmDialog.resolveCallback) {
                this.confirmDialog.resolveCallback(confirmed);
                this.confirmDialog.resolveCallback = null;
            }
            this.confirmDialog.isOpen = false;
        },
        
        normalizeResultInput(raw) {
            const s = raw.trim();
            if (s.includes(':')) {
                const [xRaw, yRaw] = s.split(':');
                const x = Number(xRaw), y = Number(yRaw);
                if (
                    Number.isInteger(x) && Number.isInteger(y) &&
                    (x >= 11 || y >= 11) &&
                    Math.abs(x - y) >= 2
                ) {
                    return `${x}:${y}`;
                }
                return null;
            }

            const num = Number(s);
            if (isNaN(num)) {
                return null;
            }

            const losing = Math.abs(num);
            const winning = losing < 10 ? 11 : losing + 2;

            // Prüfe das ursprüngliche Vorzeichen vor der Konvertierung
            if (s.startsWith('-')) {
                return `${losing}:${winning}`;
            } else {
                return `${winning}:${losing}`;
            }
        },

        async loadTournamentData() {
            const tRes = await apiClient.get(
                `/tournament/${this.currentClub}/${this.selectedDate}`
            );
            const tournament = tRes.data;
            this.currentTournamentName = tournament.name || '';
            this.currentTournamentDate = tournament.date || '';
            this.currentWinningSets = tournament.winningSets || 3;
            this.isGroupTournament = tournament.type === 'groups';
            this.currentNumberOfTables = Number.isFinite(Number(tournament.numberOfTables)) ? Number(tournament.numberOfTables) : null;
            // Defensive: Backend/DB kann (historisch/UI-default) 0/null liefern.
            // Für gruppenbasierte Turniere ohne Klassen brauchen wir hier aber eine sinnvolle Zahl,
            // sonst sendet die UI später wieder `0` an `/tournament/groups`.
            const loadedGroups = Number(tournament.numberOfGroups);
            this.numberOfGroups = Number.isFinite(loadedGroups) && loadedGroups > 0 ? loadedGroups : 1;
            this.advancingPerGroup = tournament.advancingPerGroup;
            
            // Prüfe, ob es einen Trainingstag für das Turnierdatum gibt
            await this.checkTrainingForDate(tournament.date);
            // Lade Klassen
            await this.loadTournamentClasses();
            // Lade Paarungen für alle Doppel-Klassen
            await this.loadPairings();
            // Lade Teilnehmer für alle Klassen des Turniers
            // Da Teilnehmer jetzt klassengebunden sind, müssen wir sie pro Klasse laden
            const allParticipants = [];
            for (const classItem of this.tournamentClasses) {
                try {
                    const pRes = await apiClient.post('/tournament/participants', {
                        clubId: this.currentClub,
                        tournamentId: this.selectedDate,
                        classId: classItem.id
                    });
                    // Stelle sicher, dass keine Duplikate hinzugefügt werden
                    pRes.data.forEach(p => {
                        if (!allParticipants.find(existing => existing.id === p.id)) {
                            allParticipants.push(p);
                        }
                    });
                } catch (error) {
                    console.error(`Fehler beim Laden der Teilnehmer für Klasse ${classItem.id}:`, error);
                }
            }
            // Lade auch Teilnehmer ohne Klasse (falls vorhanden)
            try {
                const pRes = await apiClient.post('/tournament/participants', {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate,
                    classId: null
                });
                pRes.data.forEach(p => {
                    if (!allParticipants.find(existing => existing.id === p.id)) {
                        allParticipants.push(p);
                    }
                });
            } catch (error) {
                console.error('Fehler beim Laden der Teilnehmer ohne Klasse:', error);
            }
            const pRes = { data: allParticipants };
            // Lade Gruppen zuerst, damit wir groupNumber initialisieren können
            const gRes = await apiClient.get('/tournament/groups', {
                params: {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate
                }
            });
            // Setze Gruppen neu, um Vue-Reaktivität sicherzustellen
            const groupsData = Array.isArray(gRes.data)
                ? gRes.data
                : (Array.isArray(gRes.data?.groups) ? gRes.data.groups : []);
            this.groups = [...groupsData];
            
            // Erstelle Mapping von groupId zu groupNumber
            const groupIdToNumberMap = (this.groups || []).reduce((m, g) => {
                m[g.groupId] = g.groupNumber;
                return m;
            }, {});
            
            // Stelle sicher, dass seeded-Feld vorhanden ist (für alte Einträge)
            // Initialisiere auch groupNumber basierend auf groupId
            this.participants = (Array.isArray(pRes.data) ? pRes.data : []).map(p => ({
                ...p,
                seeded: p.seeded || false,
                groupNumber: p.groupId ? (groupIdToNumberMap[p.groupId] || null) : null
            }));
            
            // Lade externe Teilnehmer (nur bei allowsExternal = true) - pro Klasse
            if (this.allowsExternal) {
                try {
                    const allExternalParticipants = [];
                    for (const classItem of this.tournamentClasses) {
                        try {
                            const extRes = await apiClient.post('/tournament/external-participants', {
                                clubId: this.currentClub,
                                tournamentId: this.selectedDate,
                                classId: classItem.id
                            });
                            // Stelle sicher, dass keine Duplikate hinzugefügt werden
                            extRes.data.forEach(p => {
                                if (!allExternalParticipants.find(existing => existing.id === p.id)) {
                                    allExternalParticipants.push(p);
                                }
                            });
                        } catch (error) {
                            console.error(`Fehler beim Laden der externen Teilnehmer für Klasse ${classItem.id}:`, error);
                        }
                    }
                    // Lade auch externe Teilnehmer ohne Klasse (falls vorhanden)
                    try {
                        const extRes = await apiClient.post('/tournament/external-participants', {
                            clubId: this.currentClub,
                            tournamentId: this.selectedDate,
                            classId: null
                        });
                        extRes.data.forEach(p => {
                            if (!allExternalParticipants.find(existing => existing.id === p.id)) {
                                allExternalParticipants.push(p);
                            }
                        });
                    } catch (error) {
                        console.error('Fehler beim Laden der externen Teilnehmer ohne Klasse:', error);
                    }
                    this.externalParticipants = (Array.isArray(allExternalParticipants) ? allExternalParticipants : []).map(p => ({
                        ...p,
                        seeded: p.seeded || false,
                        isExternal: true,
                        groupNumber: p.groupId ? (groupIdToNumberMap[p.groupId] || null) : null
                    }));
                } catch (error) {
                    console.error('Fehler beim Laden der externen Teilnehmer:', error);
                    this.externalParticipants = [];
                }
            } else {
                this.externalParticipants = [];
            }
            const mRes = await apiClient.get(
                `/tournament/matches/${this.currentClub}/${this.selectedDate}`
            );
            const grpMap = this.groups.reduce((m, g) => {
                m[g.groupId] = {
                    groupNumber: g.groupNumber,
                    groupLabel: g.groupLabel || null,
                    stageId: g.stageId || null,
                    stageName: g.stageName || null
                };
                return m;
            }, {});
            
            this.matches = mRes.data.map(m => {
                // Verwende groupId aus dem Backend, falls vorhanden, sonst aus den Spielern
                const matchGroupId = m.groupId || m.player1?.groupId || m.player2?.groupId;
                
                // Stelle sicher, dass groupRound vorhanden ist (kann als group_round vom Backend kommen)
                const groupRound = m.groupRound || m.group_round || 0;
                
                const groupInfo = grpMap[matchGroupId] || {};
                const groupNumber = groupInfo.groupNumber || 0;
                
                return {
                    ...m,
                    groupId: matchGroupId,
                    groupNumber: groupNumber,
                    groupLabel: groupInfo.groupLabel || null,
                    stageId: m.stageId || groupInfo.stageId || null,
                    stageName: groupInfo.stageName || null,
                    groupRound: groupRound, // Stelle sicher, dass groupRound gesetzt ist
                    resultInput: '',
                    isActive: m.isActive || false
                };
            });
            
            // Initialisiere groupNumber für Teilnehmer basierend auf groupId
            this.initializeParticipantGroupNumbers();
            
            // Setze Kollaps-Status: ausgeklappt wenn keine Spiele, eingeklappt wenn Spiele vorhanden
            this.showParticipants = this.matches.length === 0;
            
            this.showKnockout = this.matches.some(m => m.round !== 'group')
                || (this.groups || []).some(g => g.stageId !== null && g.stageId !== undefined);
        },

        async loadMatches() {
            // Lade nur die Matches, ohne die Teilnehmer-Daten zu überschreiben
            const mRes = await apiClient.get(
                `/tournament/matches/${this.currentClub}/${this.selectedDate}`
            );
            const grpMap = this.groups.reduce((m, g) => {
                m[g.groupId] = {
                    groupNumber: g.groupNumber,
                    groupLabel: g.groupLabel || null,
                    stageId: g.stageId || null,
                    stageName: g.stageName || null
                };
                return m;
            }, {});
            
            this.matches = mRes.data.map(m => {
                // Verwende groupId aus dem Backend, falls vorhanden, sonst aus den Spielern
                const matchGroupId = m.groupId || m.player1?.groupId || m.player2?.groupId;
                
                // Stelle sicher, dass groupRound vorhanden ist (kann als group_round vom Backend kommen)
                const groupRound = m.groupRound || m.group_round || 0;
                
                const groupInfo = grpMap[matchGroupId] || {};
                const groupNumber = groupInfo.groupNumber || 0;
                
                return {
                    ...m,
                    groupId: matchGroupId,
                    groupNumber: groupNumber,
                    groupLabel: groupInfo.groupLabel || null,
                    stageId: m.stageId || groupInfo.stageId || null,
                    stageName: groupInfo.stageName || null,
                    groupRound: groupRound,
                    resultInput: '',
                    isActive: m.isActive || false
                };
            });
            
            // Setze Kollaps-Status: ausgeklappt wenn keine Spiele, eingeklappt wenn Spiele vorhanden
            this.showParticipants = this.matches.length === 0;
            
            this.showKnockout = this.matches.some(m => m.round !== 'group')
                || (this.groups || []).some(g => g.stageId !== null && g.stageId !== undefined);
        },
        
        async handleTournamentChanged(data) {
            if (!data || !data.tournamentId) {
                return;
            }
            // Nur aktualisieren, wenn das aktuelle Turnier betroffen ist
            if (this.selectedDate && this.selectedDate !== 'new' && String(this.selectedDate) === String(data.tournamentId)) {
                try {
                    await this.loadTournamentData();
                    // Force Vue update
                    this.$forceUpdate();
                } catch (error) {
                    console.error('Fehler beim Neuladen der Turnier-Daten:', error);
                }
            } else {
                // Wenn ein neues Turnier erstellt wurde, lade die Turnierliste neu
                if (data.tournamentId && this.selectedDate === 'new') {
                    try {
                        const d = await apiClient.get(`/tournament/${this.currentClub}`);
                        // Filtere Turniere basierend auf allowsExternal Prop
                        if (this.allowsExternal) {
                            this.dates = d.data.filter(t => t.allowsExternal === true || t.allowsExternal === 1);
                        } else {
                            this.dates = d.data.filter(t => !t.allowsExternal || t.allowsExternal === false || t.allowsExternal === 0);
                        }
                    } catch (error) {
                        console.error('Fehler beim Neuladen der Turnierliste:', error);
                    }
                }
            }
        },

        async handleTablesDistributed(updated) {
            console.debug('[TournamentTab.handleTablesDistributed] updatedCount=', Array.isArray(updated) ? updated.length : 0);
            try {
                // Wenn updated mitgegeben wurde, aktualisiere lokale Matches sofort (tableNumber + isActive)
                if (Array.isArray(updated) && updated.length > 0) {
                    const map = new Map(updated.map(u => [Number(u.id), u]));
                    this.matches = this.matches.map(m => {
                        const found = map.get(Number(m.id));
                        if (found) {
                            console.debug('[TournamentTab] apply update to match', m.id, '-> tableNumber=', found.tableNumber || found.table_number);
                            return { ...m, tableNumber: found.tableNumber || found.table_number || m.tableNumber, isActive: true };
                        }
                        return m;
                    });
                    // Force re-render so CSS highlight applies
                    this.$forceUpdate();
                }
            } catch (err) {
                console.error('[handleTablesDistributed] Error updating local matches:', err);
            }
            // Lade die Turnierdaten zur endgültigen Synchronisation
            try { await this.loadTournamentData(); } catch (e) { /* ignore */ }
        },

        getPlayerName(p) {
            // Unterstütze sowohl interne (mit member) als auch externe Teilnehmer
            if (p.member) {
                return p.member.firstName + ' ' + p.member.lastName;
            } else {
                return (p.firstName || '') + ' ' + (p.lastName || '');
            }
        },

        labelGender(g) {
            const v = (g || 'unknown');
            if (v === 'male') return 'Männlich';
            if (v === 'female') return 'Weiblich';
            if (v === 'diverse') return 'Divers';
            return 'Unbekannt';
        },
        genderSymbol(g) {
            const v = (g || 'unknown');
            if (v === 'male') return '♂';
            if (v === 'female') return '♀';
            if (v === 'diverse') return '⚧';
            return '';
        },
        getMatchPlayerNames(match) {
            // Prüfe, ob das Match zu einer Doppel-Klasse gehört
            const classId = match.classId;
            if (classId) {
                const tournamentClass = this.tournamentClasses.find(c => c.id === classId);
                if (tournamentClass && tournamentClass.isDoubles) {
                    // Bei Doppel: Finde die Paarungen für player1Id und player2Id
                    const pairing1 = this.pairings.find(p => 
                        p.classId === classId &&
                        (p.member1Id === match.player1Id || p.external1Id === match.player1Id ||
                         p.member2Id === match.player1Id || p.external2Id === match.player1Id)
                    );
                    const pairing2 = this.pairings.find(p => 
                        p.classId === classId &&
                        (p.member1Id === match.player2Id || p.external1Id === match.player2Id ||
                         p.member2Id === match.player2Id || p.external2Id === match.player2Id)
                    );
                    
                    if (pairing1 && pairing2) {
                        const name1 = this.getPairingPlayerName(pairing1, 1) + ' / ' + this.getPairingPlayerName(pairing1, 2);
                        const name2 = this.getPairingPlayerName(pairing2, 1) + ' / ' + this.getPairingPlayerName(pairing2, 2);
                        return { name1, name2 };
                    }
                }
            }
            // Bei Einzel: Normale Anzeige
            return {
                name1: this.getPlayerName(match.player1),
                name2: this.getPlayerName(match.player2)
            };
        },

        async loadTournaments() {
            try {
                const d = await apiClient.get(`/tournament/${this.currentClub}`);
                // Filtere Turniere basierend auf allowsExternal Prop
                if (this.allowsExternal) {
                    this.dates = d.data.filter(t => t.allowsExternal === true || t.allowsExternal === 1);
                } else {
                    this.dates = d.data.filter(t => !t.allowsExternal || t.allowsExternal === false || t.allowsExternal === 0);
                }
                
                // Lade Mitgliederliste, falls noch nicht geladen
                if (!this.clubMembers || this.clubMembers.length === 0) {
                    try {
                        const m = await apiClient.get(
                            `/clubmembers/get/${this.currentClub}/false`
                        );
                        // Sortiere alphabetisch: zuerst nach Vorname, dann nach Nachname
                        const sorted = (m.data || []).sort((a, b) => {
                            const firstNameA = (a.firstName || '').toLowerCase();
                            const firstNameB = (b.firstName || '').toLowerCase();
                            if (firstNameA !== firstNameB) {
                                return firstNameA.localeCompare(firstNameB, 'de');
                            }
                            const lastNameA = (a.lastName || '').toLowerCase();
                            const lastNameB = (b.lastName || '').toLowerCase();
                            return lastNameA.localeCompare(lastNameB, 'de');
                        });
                        this.clubMembers = sorted;
                    } catch (error) {
                        console.error('Fehler beim Laden der Mitgliederliste:', error);
                        this.clubMembers = [];
                    }
                }
                
                // Prüfe nicht hier, da kein Turnier ausgewählt ist
            } catch (error) {
                console.error('Fehler beim Laden der Turniere:', error);
                this.dates = [];
            }
        },

        async checkTrainingForDate(date) {
            try {
                if (!date) {
                    this.hasTrainingToday = false;
                    return;
                }
                // Konvertiere das Datum ins YYYY-MM-DD Format, falls es noch nicht in diesem Format ist
                const dateStr = date instanceof Date 
                    ? date.toISOString().split('T')[0] 
                    : date.split('T')[0]; // Falls es ein ISO-String ist, nimm nur den Datumsteil
                
                const response = await apiClient.get(`/diary/${this.currentClub}`);
                
                // Die API gibt alle Trainingstage zurück, filtere nach dem Turnierdatum
                const trainingData = response.data;
                if (Array.isArray(trainingData)) {
                    this.hasTrainingToday = trainingData.some(training => training.date === dateStr);
                } else {
                    this.hasTrainingToday = false;
                }
            } catch (error) {
                console.error('Fehler beim Prüfen des Trainingstags:', error);
                this.hasTrainingToday = false;
            }
        },

        async updateTournament() {
            if (!this.currentTournamentDate) {
                // Wenn kein Datum vorhanden ist, nicht speichern (z.B. beim ersten Laden)
                return;
            }
            try {
                await apiClient.put(`/tournament/${this.currentClub}/${this.selectedDate}`, {
                    name: this.currentTournamentName || this.currentTournamentDate,
                    date: this.currentTournamentDate,
                    winningSets: this.currentWinningSets,
                    numberOfTables: this.currentNumberOfTables
                });
                // Prüfe, ob es einen Trainingstag für das neue Datum gibt
                await this.checkTrainingForDate(this.currentTournamentDate);
                // Lade Turnierliste neu, damit der Name in der Dropdown-Liste aktualisiert wird
                await this.loadTournaments();
                // Lade Turnierdaten neu, um sicherzustellen, dass alle Werte korrekt sind
                await this.loadTournamentData();
            } catch (error) {
                console.error('Fehler beim Aktualisieren des Turniers:', error);
                const message = safeErrorMessage(error, this.$t('tournaments.errorUpdatingTournament'));
                await this.showInfo(this.$t('messages.error'), message, '', 'error');
                // Lade Daten neu, um die ursprünglichen Werte wiederherzustellen
                await this.loadTournamentData();
            }
        },

        async createTournament() {
            if (!this.newDate) {
                await this.showInfo(this.$t('messages.error'), this.$t('tournaments.pleaseEnterDate'), '', 'error');
                return;
            }
            try {
                const r = await apiClient.post('/tournament', {
                    clubId: this.currentClub,
                    tournamentName: this.newTournamentName || this.newDate,
                    date: this.newDate,
                    winningSets: this.newWinningSets,
                    allowsExternal: this.allowsExternal
                });
                
                
                // Speichere die ID des neuen Turniers
                const newTournamentId = r.data.id;
                
                // Lade die Turniere neu
                await this.loadTournaments();
                
                // Setze das neue Turnier als ausgewählt
                this.selectedDate = newTournamentId;
                
                this.newDate = '';
                this.newTournamentName = '';
                this.newWinningSets = 3;
            } catch (error) {
                console.error('Fehler beim Erstellen des Turniers:', error);
                const message = safeErrorMessage(error, this.$t('tournaments.errorCreatingTournament'));
                await this.showInfo(this.$t('messages.error'), message, '', 'error');
            }
        },

        async addParticipant() {
            if (!this.selectedMember) {
                await this.showInfo(this.$t('messages.error'), this.$t('tournaments.pleaseSelectParticipant'), '', 'error');
                return;
            }
            const classId = this.activeAssignmentClassId;
            if (classId === undefined) {
                await this.showInfo(this.$t('messages.error'), this.$t('tournaments.selectClassPrompt'), '', 'error');
                return;
            }
            try {
                const oldMap = this.participants.reduce((map, p) => {
                    map[p.id] = p.groupNumber;
                    return map;
                }, {});
                const r = await apiClient.post('/tournament/participant', {
                    clubId: this.currentClub,
                    // Wenn ohne Klasse hinzugefügt wird, braucht das Backend die Turnier-ID.
                    // (Bei klassengebundenen Teilnehmern ist sie optional, schadet aber nicht.)
                    tournamentId: this.selectedDate,
                    classId: classId,
                    participant: this.selectedMember
                });
                
                // Prüfe, ob r.data ein Array ist
                if (Array.isArray(r.data)) {
                    this.participants = r.data.map(p => ({
                        ...p,
                        groupNumber:
                            oldMap[p.id] != null
                                ? oldMap[p.id]
                                : (p.groupId || null)
                    }));
                } else {
                    // Falls r.data kein Array ist, lade die Teilnehmerliste neu
                    await this.loadParticipants();
                }
                
                // Nur selectedMember zurücksetzen
                this.selectedMember = null;
                // Lade Paarungen neu, falls eine Doppel-Klasse ausgewählt ist
                if (this.selectedViewClass !== null && this.selectedViewClass !== undefined && this.selectedViewClass !== '__none__' && this.isClassDoubles(this.selectedViewClass)) {
                    await this.loadPairings();
                }
            } catch (error) {
                console.error('Fehler beim Hinzufügen des Teilnehmers:', error);
                const message = safeErrorMessage(error, 'Fehler beim Hinzufügen des Teilnehmers.');
                await this.showInfo(this.$t('messages.error'), message, '', 'error');
                // Lade die Teilnehmerliste neu, um den aktuellen Stand zu haben
                await this.loadParticipants();
            }
        },

        async createGroups() {
            try {
                // Wenn Klassen vorhanden sind, verwende groupsPerClass.
                // Achtung: Auch für "Ohne Klasse" (selectedViewClass='__none__') ist das der richtige Pfad,
                // sonst fällt die UI in den alten Fallback und sendet `numberOfGroups` (häufig 1).
                if (this.tournamentClasses.length > 0 || this.selectedViewClass === '__none__') {
                    await apiClient.post('/tournament/groups/create', {
                        clubId: this.currentClub,
                        tournamentId: this.selectedDate,
                        groupsPerClass: this.groupsPerClass
                    });
                } else {
                    // Fallback: Verwende numberOfGroups wie bisher
                    const desired = Math.max(1, parseInt(String(this.numberOfGroups), 10) || 1);
                    await apiClient.put('/tournament/groups', {
                        clubId: this.currentClub,
                        tournamentId: this.selectedDate,
                        numberOfGroups: desired
                    });
                }
                await this.loadTournamentData();
            } catch (error) {
                console.error(this.$t('tournaments.errorCreatingGroups'), error);
                const message = error.response?.data?.error || error.message || this.$t('tournaments.errorCreatingGroups');
                await this.showInfo(this.$t('messages.error'), message, '', 'error');
            }
        },

        async randomizeGroups() {
            try {
                const r = await apiClient.post('/tournament/groups', {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate
                });
                this.participants = r.data;
            } catch (err) {
                const message = safeErrorMessage(err, 'Fehler beim Zufällig-Verteilen.');
                await this.showInfo('Fehler', message, '', 'error');
            }
            await this.loadTournamentData();
        },

        async saveMatchResult(match, result) {
            if (!result || result.trim().length === 0) {
                return;
            }
            const normalized = this.normalizeResultInput(result);
            if (!normalized) return;
            result = normalized;
            await apiClient.post('/tournament/match/result', {
                clubId: this.currentClub,
                tournamentId: this.selectedDate,
                matchId: match.id,
                set: (match.tournamentResults?.length || 0) + 1,
                result
            });
            // Lade Turnierdaten neu, da das Match möglicherweise automatisch abgeschlossen wurde
            await this.loadTournamentData();
        },

        async deleteMatchSet(match, set) {
            try {
                await apiClient.delete('/tournament/match/result', {
                    data: {
                        clubId: this.currentClub,
                        tournamentId: this.selectedDate,
                        matchId: match.id,
                        set: set,
                    }
                });
            } catch (err) {
                const message = safeErrorMessage(err, 'Fehler beim Löschen des Satzes.');
                await this.showInfo('Fehler', message, '', 'error');
            }
            await this.loadTournamentData();
        },

        async finishMatch(match) {
            try {
                await apiClient.post('/tournament/match/finish', {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate,
                    matchId: match.id
                });
                await this.loadTournamentData();

                // Nach dem Neuladen die aktualisierte Match‑Info holen und anzeigen (inkl. Tisch)
                const updated = (this.matches || []).find(m => m.id === match.id) || match;
                const names = this.getMatchPlayerNames(updated);
                const table = (updated && updated.tableNumber != null) ? updated.tableNumber : (match.tableNumber != null ? match.tableNumber : '-');
                const title = this.$t ? this.$t('messages.info') : 'Info';
                const body = `${names.name1} – ${names.name2}\n${this.$t ? this.$t('tournaments.numberOfTables') : 'Tisch'}: ${table}`;
                if (this.$root && this.$root.showInfo) {
                    this.$root.showInfo(title, body);
                }
            } catch (err) {
                const message = safeErrorMessage(err, 'Fehler beim Abschließen des Matches.');
                await this.showInfo('Fehler', message, '', 'error');
            }
        },

        async setMatchActive(match, isActive) {
            try {
                await apiClient.put(`/tournament/match/${this.currentClub}/${this.selectedDate}/${match.id}/active`, {
                    isActive: isActive
                });
                // Lade Turnierdaten neu, um sicherzustellen, dass der Status korrekt ist
                await this.loadTournamentData();
            } catch (error) {
                console.error('Fehler beim Setzen des Match-Status:', error);
                await this.loadTournamentData();
            }
        },

        async generatePDF() {
            if (!this.selectedDate || this.selectedDate === 'new') {
                await this.showInfo('Hinweis', 'Bitte wählen Sie zuerst ein Turnier aus.', '', 'info');
                return;
            }

            try {
                const pdf = new PDFGenerator(20, 10, this.$t);
                
                // Bereite Daten vor - erstelle Mapping von groupId zu classId
                const groupIdToClassId = {};
                this.groups.forEach(group => {
                    groupIdToClassId[group.groupId] = group.classId;
                });

                const matchesByClassAndGroup = {};
                this.groupMatches.forEach(match => {
                    // Hole classId aus der Gruppe
                    const classId = match.classId != null ? String(match.classId) : 
                                   (groupIdToClassId[match.groupId] != null ? String(groupIdToClassId[match.groupId]) : 'null');
                    const groupId = String(match.groupId || 'null');
                    
                    if (!matchesByClassAndGroup[classId]) {
                        matchesByClassAndGroup[classId] = {};
                    }
                    if (!matchesByClassAndGroup[classId][groupId]) {
                        matchesByClassAndGroup[classId][groupId] = [];
                    }
                    matchesByClassAndGroup[classId][groupId].push(match);
                });

                // Sortiere Matches innerhalb jeder Gruppe nach Runde
                Object.values(matchesByClassAndGroup).forEach(groups => {
                    Object.values(groups).forEach(matches => {
                        matches.sort((a, b) => {
                            if (a.groupRound !== b.groupRound) {
                                return (a.groupRound || 0) - (b.groupRound || 0);
                            }
                            return (a.id || 0) - (b.id || 0);
                        });
                    });
                });

                // Erstelle eine Funktion, um Match-Ergebnisse zu finden
                const findMatchResult = (player1Id, player2Id, groupId) => {
                    const match = this.groupMatches.find(m => 
                        m.groupId === groupId &&
                        ((m.player1.id === player1Id && m.player2.id === player2Id) ||
                         (m.player1.id === player2Id && m.player2.id === player1Id)) &&
                        m.isFinished
                    );
                    if (!match) return null;
                    return match.result || this.formatResult(match);
                };

                // Erstelle eine Funktion, um Klassennamen zu finden
                const getClassNameForId = (classId) => {
                    if (classId === 'null' || classId === 'undefined') return null;
                    const classItem = this.tournamentClasses.find(c => c.id === parseInt(classId));
                    return classItem ? classItem.name : null;
                };

                // Erweitere PDFGenerator mit den benötigten Funktionen
                pdf.getClassNameForId = (classId) => getClassNameForId(classId);
                pdf.findMatchResult = findMatchResult;

                // Generiere PDF
                // Verwende extendedRankingList wenn K.O.-Runden vorhanden sind, sonst rankingList
                const rankingToUse = this.knockoutMatches.length > 0 ? this.extendedRankingList : this.rankingList;
                pdf.addTournamentPDF(
                    this.currentTournamentName || 'Turnier',
                    this.currentTournamentDate,
                    this.groupsByClass,
                    this.groupRankings,
                    matchesByClassAndGroup,
                    this.getPlayerName,
                    rankingToUse,
                    this.sortedParticipants,
                    this.knockoutMatches.length > 0,
                    this.knockoutMatches
                );

                // Dateiname generieren
                const dateStr = this.currentTournamentDate 
                    ? new Date(this.currentTournamentDate).toISOString().split('T')[0]
                    : 'turnier';
                const filename = `${this.currentTournamentName || 'Turnier'}_${dateStr}.pdf`.replace(/[^a-zA-Z0-9._-]/g, '_');
                
                pdf.save(filename);
            } catch (error) {
                console.error('Fehler beim Generieren des PDFs:', error);
                await this.showInfo('Fehler', 'Fehler beim Generieren des PDFs', error.message, 'error');
            }
        },

    async startKnockout() {
            // Wenn eine Stage-Konfiguration existiert, ist /tournament/stages/advance der
            // korrekte Weg, weil nur dort die Pool-Regeln (z.B. Plätze 1,2) berücksichtigt werden.
            // Fallback ist die Legacy-Route /tournament/knockout.
            try {
                const stagesRes = await apiClient.get('/tournament/stages', {
                    params: {
                        clubId: this.currentClub,
                        tournamentId: this.selectedDate
                    }
                });

                const stages = stagesRes?.data?.stages;
                if (Array.isArray(stages) && stages.length > 0) {
                    // Backend arbeitet mit expliziten Stage-Indizes (z.B. 1 und 3), die nicht
                    // zwingend 1..N sind. Daher müssen wir die Indizes aus der Antwort ableiten.
                    const normalizedStages = stages
                        .map(s => ({
                            ...s,
                            // Prefer explicit index field; fall back to id for ordering if needed
                            stageIndex: Number(s.stageIndex ?? s.index ?? s.id),
                            stageId: Number(s.id ?? s.stageId ?? s.stageIndex)
                        }))
                        .filter(s => Number.isFinite(s.stageIndex));

                    // Ermittle die Reihenfolge der Stages und führe genau den nächsten
                    // konfigurierten Übergang aus. Die Zielrunde kann Gruppen oder K.o. sein.
                    const ordered = normalizedStages.sort((a, b) => a.stageIndex - b.stageIndex);
                    const fromStage = ordered.find(s => (s.type || s.targetType || s.target) === 'groups');
                    const toStage = fromStage
                        ? ordered.find(s => s.stageIndex > fromStage.stageIndex)
                        : null;

                    if (fromStage && toStage) {
                        const payload = {
                            clubId: this.currentClub,
                            tournamentId: this.selectedDate
                        };
                        payload.fromStageIndex = fromStage.stageIndex;
                        payload.toStageIndex = toStage.stageIndex;
                        await apiClient.post('/tournament/stages/advance', payload);
                        await this.loadTournamentData();
                        return;
                    }
                }
            } catch (e) {
                // Ignorieren und Legacy-Fallback nutzen.
                // (z.B. wenn Endpoint nicht verfügbar oder Stages nicht konfiguriert)
            }

            await apiClient.post('/tournament/knockout', {
                clubId: this.currentClub,
                tournamentId: this.selectedDate
            });
            await this.loadTournamentData();
        },

        formatResult(match) {
            if (!match.tournamentResults?.length) return '-';
            return match.tournamentResults
                .sort((a, b) => a.set - b.set)
                .map(r => `${Math.abs(r.pointsPlayer1)}:${Math.abs(r.pointsPlayer2)}`)
                .join(', ');
        },

        async startMatches() {
            if (this.isGroupTournament) {
                if (!this.groups.length) {
                    await this.createGroups();
                }
                await this.randomizeGroups();
            } else {
                await this.startKnockout();
            }
            await this.loadTournamentData();
        },

        async createMatches() {
            if (!this.isGroupTournament) {
                return;
            }
            if (!this.groups.length) {
                await this.createGroups();
            }
            // Übergebe classId, wenn eine Klasse ausgewählt ist (nicht '__none__' oder null)
            const classId = (this.selectedViewClass && this.selectedViewClass !== '__none__' && this.selectedViewClass !== null) 
                ? this.selectedViewClass 
                : null;
            await apiClient.post('/tournament/matches/create', {
                clubId: this.currentClub,
                tournamentId: this.selectedDate,
                classId: classId
            });
            await this.loadTournamentData();
        },

        async onModusChange() {
            const type = this.isGroupTournament ? 'groups' : 'knockout';
            const desired = Math.max(1, parseInt(String(this.numberOfGroups), 10) || 1);
            await apiClient.post('/tournament/modus', {
                clubId: this.currentClub,
                tournamentId: this.selectedDate,
                type,
                numberOfGroups: desired,
                advancingPerGroup: this.advancingPerGroup
            });
            await this.loadTournamentData();
        },

        async resetGroups() {
            await apiClient.post('/tournament/groups/reset', {
                clubId: this.currentClub,
                tournamentId: this.selectedDate
            });
            await this.loadTournamentData();
        },

        async resetMatches() {
            // Übergebe classId, wenn eine Klasse ausgewählt ist (nicht '__none__' oder null)
            const classId = (this.selectedViewClass && this.selectedViewClass !== '__none__' && this.selectedViewClass !== null) 
                ? this.selectedViewClass 
                : null;
            await apiClient.post('/tournament/matches/reset', {
                clubId: this.currentClub,
                tournamentId: this.selectedDate,
                classId: classId
            });
            await this.loadTournamentData();
        },

        async removeParticipant(p) {
            try {
                if (this.allowsExternal && p.isExternal) {
                    // Externer Teilnehmer
                    await apiClient.delete('/tournament/external-participant', {
                        data: {
                            clubId: this.currentClub,
                            tournamentId: this.selectedDate,
                            participantId: p.id
                        }
                    });
                    this.externalParticipants = this.externalParticipants.filter(x => x.id !== p.id);
                } else {
                    // Interner Teilnehmer
                    await apiClient.delete('/tournament/participant', {
                        data: {
                            clubId: this.currentClub,
                            tournamentId: this.selectedDate,
                            participantId: p.id
                        }
                    });
                    this.participants = this.participants.filter(x => x.id !== p.id);
                }
                await this.loadTournamentData();
            } catch (error) {
                console.error('Fehler beim Entfernen des Teilnehmers:', error);
                const message = safeErrorMessage(error, 'Fehler beim Entfernen des Teilnehmers.');
                await this.showInfo('Fehler', message, '', 'error');
            }
        },

        async onGroupCountChange() {
            // Wenn Klassen vorhanden sind, speichere groupsPerClass, sonst numberOfGroups
            // Hinweis: Bei "Ohne Klasse" wird groupsPerClass['null'] genutzt, auch wenn Klassen existieren.
            if (this.tournamentClasses.length > 0 || this.selectedViewClass === '__none__') {
                // Speichere groupsPerClass für die aktuelle Klasse
                // Die Werte sind bereits in this.groupsPerClass gespeichert durch den setter von groupsPerClassInput
                // Wir müssen nichts speichern, da groupsPerClass nur lokal verwendet wird
                // Die tatsächliche Erstellung der Gruppen erfolgt über createGroups
                return;
            } else {
                // Fallback: Verwende numberOfGroups wie bisher
                const desired = Math.max(1, parseInt(String(this.numberOfGroups), 10) || 1);
                await apiClient.post('/tournament/modus', {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate,
                    type: this.isGroupTournament ? 'groups' : 'knockout',
                    numberOfGroups: desired
                });
                await this.loadTournamentData();
            }
        },

        async reopenMatch(match) {
            await apiClient.post('/tournament/match/reopen', {
                clubId: this.currentClub,
                tournamentId: this.selectedDate,
                matchId: match.id
            });
            await this.loadTournamentData();
        },

        async deleteResult(match, set) {
            if (match.isFinished) {
                await this.reopenMatch(match);
                match.isFinished = false;
            }
            await apiClient.delete('/tournament/match/result', {
                data: {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate,
                    matchId: match.id,
                    set
                }
            });
            await this.loadTournamentData();
        },

        startEditResult(match, result) {
            if (match.isFinished) {
                this.reopenMatch(match);
                match.isFinished = false;
            }
            this.editingResult.matchId = match.id;
            this.editingResult.set = result.set;
            this.editingResult.value = `${result.pointsPlayer1}:${result.pointsPlayer2}`;
            this.activeMatchId = match.id;  // Setze aktives Match für Hervorhebung
            
            // Fokussiere das Eingabefeld nach dem nächsten DOM-Update
            this.$nextTick(() => {
                const editInputs = this.$refs.editInput;
                if (editInputs && editInputs.length > 0) {
                    const lastInput = editInputs[editInputs.length - 1];
                    lastInput.focus();
                    lastInput.select();
                }
            });
        },

        async saveEditedResult(match) {
            const { set, value } = this.editingResult;
            const normalized = this.normalizeResultInput(value);
            if (!normalized) {
                this.cancelEdit();
                return;
            }
            let result = normalized;
            await apiClient.post('/tournament/match/result', {
                clubId: this.currentClub,
                tournamentId: this.selectedDate,
                matchId: match.id,
                set,
                result
            });
            this.editingResult.matchId = null;
            this.editingResult.set = null;
            this.editingResult.value = '';
            await this.loadTournamentData();
        },

        cancelEdit() {
            this.editingResult.matchId = null;
            this.editingResult.set = null;
            this.editingResult.value = '';
        },

        isEditing(match, set) {
            return (
                this.editingResult.matchId === match.id &&
                this.editingResult.set === set
            );
        },

        isLastResult(match, result) {
            const arr = match.tournamentResults || [];
            return arr.length > 0 && arr[arr.length - 1].set === result.set;
        },

        getSetsString(match) {
            const results = match.tournamentResults || [];
            let win1 = 0, win2 = 0;
            for (const r of results) {
                if (r.pointsPlayer1 > r.pointsPlayer2) win1++;
                else if (r.pointsPlayer2 > r.pointsPlayer1) win2++;
            }
            return `${win1}:${win2}`;
        },

        winnerIsPlayer1(match) {
            const [w1, w2] = this.getSetsString(match).split(':').map(Number);
            return w1 > w2;
        },

        async resetKnockout() {
            try {
                const payload = {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate
                };
                if (this.selectedViewClass != null && this.selectedViewClass !== '__none__') {
                    payload.classId = Number(this.selectedViewClass);
                }
                await apiClient.delete('/tournament/matches/knockout', { data: payload });
                await this.loadTournamentData();
            } catch (err) {
                const message = safeErrorMessage(err, this.$t('tournaments.errorResettingKORound'));
                await this.showInfo(this.$t('messages.error'), message, '', 'error');
            }
        },

        getMatchResult(player1Id, player2Id, groupId) {
            const match = this.matches.find(m => 
                m.round === 'group' && 
                m.groupId === groupId &&
                ((m.player1.id === player1Id && m.player2.id === player2Id) ||
                 (m.player1.id === player2Id && m.player2.id === player1Id)) &&
                m.isFinished
            );
            
            if (!match) return null;
            
            // Bestimme, wer gewonnen hat
            const [sets1, sets2] = match.result.split(':').map(n => +n);
            const player1Won = sets1 > sets2;
            
            // Gib das Ergebnis in der Sicht des ersten Spielers zurück
            if (match.player1.id === player1Id) {
                return player1Won ? 'W' : 'L';
            } else {
                return player1Won ? 'L' : 'W';
            }
        },

        getMatchLiveResult(player1Id, player2Id, groupId) {
            const match = this.matches.find(m => 
                m.round === 'group' && 
                m.groupId === groupId &&
                ((m.player1.id === player1Id && m.player2.id === player2Id) ||
                 (m.player1.id === player2Id && m.player2.id === player1Id))
            );
            
            if (!match) return null;
            
            // Berechne aktuelle Sätze aus tournamentResults
            let sets1 = 0, sets2 = 0;
            if (match.tournamentResults && match.tournamentResults.length > 0) {
                match.tournamentResults.forEach(result => {
                    if (result.pointsPlayer1 > result.pointsPlayer2) {
                        sets1++;
                    } else if (result.pointsPlayer2 > result.pointsPlayer1) {
                        sets2++;
                    }
                });
            }
            
            // Bestimme die Anzeige basierend auf der Spieler-Reihenfolge
            if (match.player1.id === player1Id) {
                return {
                    sets1: sets1,
                    sets2: sets2,
                    isFinished: match.isFinished,
                    player1Won: sets1 > sets2,
                    player2Won: sets2 > sets1,
                    isTie: sets1 === sets2
                };
            } else {
                return {
                    sets1: sets2,
                    sets2: sets1,
                    isFinished: match.isFinished,
                    player1Won: sets2 > sets1,
                    player2Won: sets1 > sets2,
                    isTie: sets1 === sets2
                };
            }
        },

        highlightMatch(player1Id, player2Id, groupId) {
            
            // Finde das entsprechende Match (auch unbeendete)
            const match = this.matches.find(m => 
                m.round === 'group' && 
                m.groupId === groupId &&
                ((m.player1.id === player1Id && m.player2.id === player2Id) ||
                 (m.player1.id === player2Id && m.player2.id === player1Id))
            );
            
            
            if (!match) {
                return;
            }
            
            // Setze activeMatchId für bidirektionale Hervorhebung
            this.activeMatchId = match.id;
            
            // Setze Highlight-Klasse
            this.$nextTick(() => {
                const matchElement = document.querySelector(`tr[data-match-id="${match.id}"]`);
                
                if (matchElement) {
                    // Entferne vorherige Highlights
                    document.querySelectorAll('.match-highlight').forEach(el => {
                        el.classList.remove('match-highlight');
                    });
                    
                    // Füge Highlight hinzu
                    matchElement.classList.add('match-highlight');
                    
                    // Scrolle zum Element
                    matchElement.scrollIntoView({ 
                        behavior: 'smooth', 
                        block: 'center' 
                    });
                } else {
                }
            });
        },

        clearHighlight() {
            // Entferne alle Highlights
            document.querySelectorAll('.match-highlight').forEach(el => {
                el.classList.remove('match-highlight');
            });
        },

        async updateParticipantSeeded(participant, event) {
            const seeded = event.target.checked;
            
            // Aktualisiere lokal
            participant.seeded = seeded;
            
            // Sende an Backend
            try {
                if (this.allowsExternal && participant.isExternal) {
                    // Externer Teilnehmer
                    await apiClient.put(`/tournament/external-participant/${this.currentClub}/${this.selectedDate}/${participant.id}/seeded`, {
                        seeded: seeded
                    });
                } else {
                    // Interner Teilnehmer
                    await apiClient.put(`/tournament/participant/${this.currentClub}/${this.selectedDate}/${participant.id}/seeded`, {
                        seeded: seeded
                    });
                }
            } catch (error) {
                console.error('Fehler beim Aktualisieren des Gesetzt-Status:', error);
                // Bei Fehler: Lade Daten neu
                await this.loadTournamentData();
            }
        },

        async updateParticipantGroup(participant, event) {
            const value = event.target.value;
            const groupNumber = value === '' || value === 'null' ? null : parseInt(value);
            
            console.log('[updateParticipantGroup] Updating participant:', participant.id, 'to groupNumber:', groupNumber, 'isExternal:', participant.isExternal);
            
            // Speichere die alte groupNumber für den Fall eines Fehlers
            const oldGroupNumber = participant.groupNumber;
            
            // Aktualisiere lokal sofort für responsive UI (mit Vue-Reaktivität)
            if (this.$set) {
                // Vue 2
                this.$set(participant, 'groupNumber', groupNumber);
            } else {
                // Vue 3
                participant.groupNumber = groupNumber;
            }
            
            // Sende nur diesen einen Teilnehmer an Backend
            try {
                const response = await apiClient.put('/tournament/participant/group', {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate,
                    participantId: participant.id,
                    groupNumber: groupNumber,
                    isExternal: participant.isExternal || false
                });
                console.log('[updateParticipantGroup] Response:', response.data);
                
                // Verwende die Response-Daten direkt, um die Gruppen zu aktualisieren
                if (response.data && Array.isArray(response.data)) {
                    // Setze Gruppen neu, um Vue-Reaktivität sicherzustellen
                    this.groups = [...response.data];
                    console.log('[updateParticipantGroup] Updated groups:', this.groups);
                } else {
                    // Fallback: Lade Gruppen neu
                    const gRes = await apiClient.get('/tournament/groups', {
                        params: {
                            clubId: this.currentClub,
                            tournamentId: this.selectedDate
                        }
                    });
                    const groupsData = Array.isArray(gRes.data)
                        ? gRes.data
                        : (Array.isArray(gRes.data?.groups) ? gRes.data.groups : []);
                    this.groups = [...groupsData];
                }
                
                // Aktualisiere auch die groupId des Teilnehmers basierend auf der neuen groupNumber
                if (groupNumber !== null) {
                    const newGroup = this.groups.find(g => g.groupNumber === groupNumber);
                    if (newGroup) {
                        if (this.$set) {
                            this.$set(participant, 'groupId', newGroup.groupId);
                        } else {
                            participant.groupId = newGroup.groupId;
                        }
                    } else {
                        // Gruppe nicht gefunden, setze groupId auf null
                        if (this.$set) {
                            this.$set(participant, 'groupId', null);
                        } else {
                            participant.groupId = null;
                        }
                    }
                } else {
                    // groupNumber ist null, also auch groupId auf null
                    if (this.$set) {
                        this.$set(participant, 'groupId', null);
                    } else {
                        participant.groupId = null;
                    }
                }
                
                // Lade Matches neu, da sich die Gruppenzuordnung geändert hat
                await this.loadMatches();
            } catch (error) {
                console.error('Fehler beim Aktualisieren der Gruppe:', error);
                // Bei Fehler: Setze groupNumber zurück auf den alten Wert
                if (this.$set) {
                    this.$set(participant, 'groupNumber', oldGroupNumber);
                } else {
                    participant.groupNumber = oldGroupNumber;
                }
                // Lade Gruppen neu, um sicherzustellen, dass groupId korrekt ist
                try {
                    const gRes = await apiClient.get('/tournament/groups', {
                        params: {
                            clubId: this.currentClub,
                            tournamentId: this.selectedDate
                        }
                    });
                    const groupsData = Array.isArray(gRes.data)
                        ? gRes.data
                        : (Array.isArray(gRes.data?.groups) ? gRes.data.groups : []);
                    this.groups = [...groupsData];
                    // Setze groupId basierend auf dem alten groupNumber
                    if (oldGroupNumber !== null) {
                        const oldGroup = this.groups.find(g => g.groupNumber === oldGroupNumber);
                        const oldGroupId = oldGroup ? oldGroup.groupId : null;
                        if (this.$set) {
                            this.$set(participant, 'groupId', oldGroupId);
                        } else {
                            participant.groupId = oldGroupId;
                        }
                    } else {
                        if (this.$set) {
                            this.$set(participant, 'groupId', null);
                        } else {
                            participant.groupId = null;
                        }
                    }
                } catch (loadError) {
                    console.error('Fehler beim Neuladen der Gruppen:', loadError);
                }
            }
        },

        // Initialisiere groupNumber basierend auf groupId
        initializeParticipantGroupNumbers() {
            // Interne Teilnehmer
            this.participants.forEach(participant => {
                if (participant.groupId) {
                    const group = this.groups.find(g => g.groupId === participant.groupId);
                    if (group) {
                        participant.groupNumber = group.groupNumber;
                    }
                }
            });
            // Externe Teilnehmer (nur bei allowsExternal = true)
            if (this.allowsExternal) {
                this.externalParticipants.forEach(participant => {
                    if (participant.groupId) {
                        const group = this.groups.find(g => g.groupId === participant.groupId);
                        if (group) {
                            participant.groupNumber = group.groupNumber;
                        }
                    }
                });
            }
        },



        togglePairings() {
            this.showPairings = !this.showPairings;
        },

        async loadTournamentClasses() {
            if (!this.selectedDate || this.selectedDate === 'new') {
                this.tournamentClasses = [];
                this.groupsPerClass = {};
                this.selectedViewClass = '__none__';
                this.showClasses = false;
                return;
            }
            try {
                const res = await apiClient.get(`/tournament/classes/${this.currentClub}/${this.selectedDate}`);
                const classes = res.data || [];
                // Stelle sicher, dass isDoubles als Boolean behandelt wird
                this.tournamentClasses = classes.map(classItem => ({
                    ...classItem,
                    isDoubles: Boolean(classItem.isDoubles)
                }));
                // Öffne die Klassen-Sektion automatisch, wenn Klassen vorhanden sind
                if (this.tournamentClasses.length > 0) {
                    this.showClasses = true;
                }
                // Initialisiere groupsPerClass für jede Klasse
                // BEHALTE bestehende Werte, setze nur fehlende auf 0
                this.tournamentClasses.forEach(classItem => {
                    if (!(classItem.id in this.groupsPerClass) && this.groupsPerClass[classItem.id] === undefined) {
                        this.groupsPerClass[classItem.id] = 0;
                    }
                });
                // Initialisiere auch für "ohne Klasse"
                if (!('null' in this.groupsPerClass) && this.groupsPerClass['null'] === undefined) {
                    this.groupsPerClass['null'] = 0;
                }
                // Bestehende Werte bleiben erhalten

                // Aktualisiere Auswahl für die Klassenanzeige
                if (this.tournamentClasses.length === 0) {
                    this.selectedViewClass = '__none__';
                } else {
                    const validIds = this.tournamentClasses.map(c => String(c.id));
                    if (this.selectedViewClass !== null && this.selectedViewClass !== '__none__' && !validIds.includes(String(this.selectedViewClass))) {
                        this.selectedViewClass = null;
                    }
                }
            } catch (error) {
                console.error('Fehler beim Laden der Klassen:', error);
                this.tournamentClasses = [];
                this.groupsPerClass = {};
                this.selectedViewClass = '__none__';
            }
        },

        async handleAddClassError(message) {
            await this.showInfo('Fehler', message, '', 'error');
        },
        
        async addClass(data = null) {
            // Verwende die übergebenen Daten, falls vorhanden, sonst die Props
            // Die Daten sollten immer vorhanden sein, da die Child-Komponente sie übergibt
            let className;
            let isDoubles;
            let gender;
            console.log(data);
            if (data && typeof data === 'object' && data !== null && 'name' in data) {
                // Daten wurden als Objekt übergeben
                className = data.name;
                isDoubles = data.isDoubles !== undefined ? data.isDoubles : this.newClassIsDoubles;
                gender = data.gender !== undefined ? data.gender : this.newClassGender;
            } else {
                // Fallback auf Props (sollte nicht passieren, aber für Sicherheit)
                className = this.newClassName;
                isDoubles = this.newClassIsDoubles;
                gender = this.newClassGender;
            }
            
            // Validiere: className muss ein String sein und nicht leer
            // Diese Validierung sollte eigentlich nicht mehr nötig sein, da die Child-Komponente bereits validiert
            // Aber wir behalten sie als Fallback
            if (typeof className !== 'string' || !className.trim()) {
                await this.showInfo('Fehler', 'Bitte geben Sie einen Klassennamen ein!', '', 'error');
                return;
            }
            try {
                const minBirthYear = data?.minBirthYear !== undefined ? data.minBirthYear : this.newClassMaxBirthYear;
                await apiClient.post(`/tournament/class/${this.currentClub}/${this.selectedDate}`, {
                    name: className.trim(),
                    isDoubles: isDoubles,
                    gender: gender,
                    minBirthYear: minBirthYear
                });
                this.newClassName = '';
                this.newClassIsDoubles = false;
                this.newClassGender = null;
                this.newClassMaxBirthYear = null;
                await this.loadTournamentClasses();
            } catch (error) {
                console.error('Fehler beim Hinzufügen der Klasse:', error);
                const message = safeErrorMessage(error, 'Fehler beim Hinzufügen der Klasse.');
                await this.showInfo('Fehler', message, '', 'error');
            }
        },

        editClass(classItem) {
            this.editingClassId = classItem.id;
            this.editingClassName = classItem.name;
            // Stelle sicher, dass isDoubles als Boolean behandelt wird
            this.editingClassIsDoubles = Boolean(classItem.isDoubles);
            this.editingClassGender = classItem.gender || null;
            this.editingClassMaxBirthYear = classItem.minBirthYear || null;
            console.log('[editClass] classItem.isDoubles:', classItem.isDoubles, 'editingClassIsDoubles:', this.editingClassIsDoubles);
            this.$nextTick(() => {
                const inputs = this.$refs.classEditInput;
                if (inputs && inputs.length > 0) {
                    const input = Array.isArray(inputs) ? inputs.find(i => i) : inputs;
                    if (input) {
                        input.focus();
                        input.select();
                    }
                }
            });
        },

        handleClassInputBlur(event, classItem) {
            // Prüfe, ob das Blur-Event durch einen Klick auf die Checkbox oder Buttons ausgelöst wurde
            const relatedTarget = event.relatedTarget;
            if (relatedTarget) {
                // Wenn das verwandte Element die Checkbox, das Label oder die Buttons sind, nicht speichern
                const isCheckbox = relatedTarget.type === 'checkbox' || 
                                   relatedTarget.closest('.class-doubles-checkbox') ||
                                   relatedTarget.closest('.class-gender-select') ||
                                   relatedTarget.closest('.btn-save-small') ||
                                   relatedTarget.closest('.btn-cancel-small');
                if (isCheckbox) {
                    return; // Nicht speichern
                }
            }
            // Normales Blur - speichern
            this.saveClassEdit(classItem);
        },

        async saveClassEdit(classItem) {
            if (!this.editingClassName || !this.editingClassName.trim()) {
                await this.showInfo('Fehler', 'Bitte geben Sie einen Klassennamen ein!', '', 'error');
                this.cancelClassEdit();
                return;
            }
            try {
                await apiClient.put(`/tournament/class/${this.currentClub}/${this.selectedDate}/${classItem.id}`, {
                    name: this.editingClassName.trim(),
                    isDoubles: this.editingClassIsDoubles,
                    gender: this.editingClassGender,
                    minBirthYear: this.editingClassMaxBirthYear
                });
                classItem.name = this.editingClassName.trim();
                classItem.isDoubles = this.editingClassIsDoubles;
                classItem.gender = this.editingClassGender;
                classItem.minBirthYear = this.editingClassMaxBirthYear;
                this.cancelClassEdit();
            } catch (error) {
                console.error('Fehler beim Aktualisieren der Klasse:', error);
                const message = safeErrorMessage(error, 'Fehler beim Aktualisieren der Klasse.');
                await this.showInfo('Fehler', message, '', 'error');
                // Lade Klassen neu, um den ursprünglichen Namen wiederherzustellen
                await this.loadTournamentClasses();
                this.cancelClassEdit();
            }
        },

        cancelClassEdit() {
            this.editingClassId = null;
            this.editingClassName = '';
            this.editingClassIsDoubles = false;
            this.editingClassGender = null;
            this.editingClassMaxBirthYear = null;
        },

        async deleteClass(classItem) {
            const confirmed = await this.showConfirm(
                'Klasse löschen',
                `Möchten Sie die Klasse "${classItem.name}" wirklich löschen?`,
                'Alle Teilnehmer werden von dieser Klasse entfernt.',
                'warning'
            );
            if (!confirmed) return;
            try {
                await apiClient.delete(`/tournament/class/${this.currentClub}/${this.selectedDate}/${classItem.id}`);
                await this.loadTournamentClasses();
                await this.loadTournamentData();
            } catch (error) {
                console.error('Fehler beim Löschen der Klasse:', error);
                const message = safeErrorMessage(error, 'Fehler beim Löschen der Klasse.');
                await this.showInfo('Fehler', message, '', 'error');
            }
        },

        async updateParticipantClass(participant, event) {
            const classId = event.target.value === '' ? null : parseInt(event.target.value);
            try {
                await apiClient.put(`/tournament/participant/${this.currentClub}/${this.selectedDate}/${participant.id}/class`, {
                    classId: classId,
                    isExternal: participant.isExternal || false
                });
                participant.classId = classId;
            } catch (error) {
                console.error('Fehler beim Aktualisieren der Klasse:', error);
                const message = safeErrorMessage(error, 'Fehler beim Aktualisieren der Klasse.');
                await this.showInfo('Fehler', message, '', 'error');
                // Lade Daten neu, um die ursprüngliche Klasse wiederherzustellen
                await this.loadTournamentData();
            }
        },

        async addExternalParticipant() {
            if (!this.newExternalParticipant.firstName || !this.newExternalParticipant.lastName) {
                await this.showInfo('Fehler', 'Bitte geben Sie mindestens Vorname und Nachname ein!', '', 'error');
                return;
            }
            const classId = this.activeAssignmentClassId;
            if (classId === undefined) {
                await this.showInfo('Fehler', this.$t('tournaments.selectClassPrompt'), '', 'error');
                return;
            }
            try {
                await apiClient.post('/tournament/external-participant', {
                    clubId: this.currentClub,
                    classId: classId,
                    firstName: this.newExternalParticipant.firstName,
                    lastName: this.newExternalParticipant.lastName,
                    club: this.newExternalParticipant.club || null,
                    birthDate: this.newExternalParticipant.birthDate || null,
                    gender: this.newExternalParticipant.gender || 'unknown'
                });
                // Leere Eingabefelder
                this.newExternalParticipant = {
                    firstName: '',
                    lastName: '',
                    club: '',
                    birthDate: null,
                    gender: 'unknown'
                };
                // Lade Daten neu
                await this.loadTournamentData();
                // Lade Paarungen neu, falls eine Doppel-Klasse ausgewählt ist
                if (this.selectedViewClass !== null && this.selectedViewClass !== undefined && this.selectedViewClass !== '__none__' && this.isClassDoubles(this.selectedViewClass)) {
                    await this.loadPairings();
                }
            } catch (error) {
                console.error('Fehler beim Hinzufügen des externen Teilnehmers:', error);
                const message = safeErrorMessage(error, 'Fehler beim Hinzufügen des externen Teilnehmers.');
                await this.showInfo('Fehler', message, '', 'error');
            }
        },

        async loadParticipantsFromTraining() {
            try {
                if (!this.canAssignClass) {
                    await this.showInfo('Hinweis', this.$t('tournaments.selectClassPrompt'), '', 'info');
                    return;
                }
                if (!this.currentTournamentDate) {
                    await this.showInfo('Hinweis', 'Kein Turnierdatum vorhanden!', '', 'info');
                    return;
                }
                
                // Konvertiere das Turnierdatum ins YYYY-MM-DD Format
                const tournamentDate = this.currentTournamentDate instanceof Date 
                    ? this.currentTournamentDate.toISOString().split('T')[0] 
                    : this.currentTournamentDate.split('T')[0]; // Falls es ein ISO-String ist, nimm nur den Datumsteil
                
                const response = await apiClient.get(`/diary/${this.currentClub}`);
                
                // Die API gibt alle Trainingstage zurück, filtere nach dem Turnierdatum
                const trainingData = response.data;
                
                if (Array.isArray(trainingData)) {
                    
                    // Finde den Trainingstag für das Turnierdatum
                    const trainingForDate = trainingData.find(training => training.date === tournamentDate);
                    
                    if (trainingForDate) {
                        // Lade die Teilnehmer für diesen Trainingstag über die Participant-API
                        const participantsResponse = await apiClient.get(`/participants/${trainingForDate.id}`);
                        
                        const allTrainingParticipants = Array.isArray(participantsResponse.data)
                            ? participantsResponse.data
                            : [];
                        const participants = allTrainingParticipants.filter(participant => participant?.attendanceStatus === 'present');
                        
                        if (participants && participants.length > 0) {
                            // Lade die Member-Details für jeden Teilnehmer
                            const trainingParticipants = [];
                            for (const participant of participants) {
                                try {
                                    // Lade Member-Details über die Member-API
                                    const memberResponse = await apiClient.get(`/clubmembers/get/${this.currentClub}/true`);
                                    const member = memberResponse.data.find(m => m.id === participant.memberId);
                                    
                                    if (member) {
                                        trainingParticipants.push({
                                            clubMemberId: participant.memberId,
                                            member: member
                                        });
                                    }
                                } catch (memberError) {
                                    console.error('Fehler beim Laden der Member-Details:', memberError);
                                }
                            }
                            
                            if (trainingParticipants.length > 0) {
                                // Füge alle Trainingsteilnehmer zum Turnier hinzu
                                for (const participant of trainingParticipants) {
                                    await this.addParticipantFromTraining(participant);
                                }
                                
                                // Lade Turnierdaten neu
                                await this.loadTournamentData();
                            } else {
                                await this.showInfo('Hinweis', 'Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!', '', 'info');
                            }
                        } else if (allTrainingParticipants.length > 0) {
                            await this.showInfo('Hinweis', 'Keine anwesenden Teilnehmer im Trainingstag für dieses Datum gefunden!', '', 'info');
                        } else {
                            await this.showInfo('Hinweis', 'Keine Teilnehmer im Trainingstag für dieses Datum gefunden!', '', 'info');
                        }
                    } else {
                        await this.showInfo('Hinweis', `Kein Trainingstag für ${tournamentDate} gefunden!`, '', 'info');
                    }
                } else {
                    await this.showInfo('Hinweis', `Kein Trainingstag für ${tournamentDate} gefunden!`, '', 'info');
                }
            } catch (error) {
                console.error('Fehler beim Laden der Trainingsteilnehmer:', error);
                const message = safeErrorMessage(error, 'Fehler beim Laden der Trainingsteilnehmer.');
                await this.showInfo('Fehler', message, '', 'error');
            }
        },

        async addParticipantFromTraining(participant) {
            try {
                const classId = this.activeAssignmentClassId;
                if (classId === undefined) {
                    return;
                }
                await apiClient.post('/tournament/participant', {
                    clubId: this.currentClub,
                    tournamentId: this.selectedDate,
                    classId: classId,
                    participant: participant.clubMemberId
                });
            } catch (error) {
                console.error('Fehler beim Hinzufügen des Teilnehmers:', error);
                // Ignoriere Fehler, da Teilnehmer möglicherweise bereits existiert
            }
        },

        async loadPairings() {
            if (!this.selectedDate || this.selectedDate === 'new') {
                this.pairings = [];
                return;
            }
            
            // Lade Paarungen für alle Doppel-Klassen, nicht nur für die ausgewählte
            const doublesClasses = this.tournamentClasses.filter(c => c.isDoubles);
            if (doublesClasses.length === 0) {
                this.pairings = [];
                return;
            }
            
            try {
                // Lade Paarungen für alle Doppel-Klassen
                const allPairings = [];
                for (const classItem of doublesClasses) {
                    try {
                        const res = await apiClient.get(`/tournament/pairings/${this.currentClub}/${this.selectedDate}/${classItem.id}`);
                        const pairings = res.data || [];
                        allPairings.push(...pairings);
                    } catch (error) {
                        console.error(`Fehler beim Laden der Paarungen für Klasse ${classItem.id}:`, error);
                    }
                }
                
                // Entferne Duplikate basierend auf ID
                const seen = new Set();
                this.pairings = allPairings.filter(p => {
                    if (seen.has(p.id)) {
                        return false;
                    }
                    seen.add(p.id);
                    return true;
                });
            } catch (error) {
                console.error('Fehler beim Laden der Paarungen:', error);
                this.pairings = [];
            }
        },

        async addPairing() {
            if (!this.newPairing.player1Id || !this.newPairing.player2Id || this.newPairing.player1Id === this.newPairing.player2Id) {
                await this.showInfo(this.$t('messages.error'), 'Bitte wählen Sie zwei verschiedene Spieler aus!', '', 'error');
                return;
            }
            // Prüfe auf Duplikate im Frontend (in beiden Richtungen)
            const existingPairing = this.pairings.find(p => {
                const p1Id = p.member1?.id || p.external1?.id;
                const p2Id = p.member2?.id || p.external2?.id;
                return (p1Id === this.newPairing.player1Id && p2Id === this.newPairing.player2Id) ||
                       (p1Id === this.newPairing.player2Id && p2Id === this.newPairing.player1Id);
            });
            if (existingPairing) {
                await this.showInfo(this.$t('messages.error'), 'Diese Paarung existiert bereits!', '', 'error');
                return;
            }
            // Ermittle automatisch, ob die Teilnehmer intern oder extern sind
            const participant1 = this.allParticipantsList().find(p => p.id === this.newPairing.player1Id);
            const participant2 = this.allParticipantsList().find(p => p.id === this.newPairing.player2Id);
            if (!participant1 || !participant2) {
                await this.showInfo(this.$t('messages.error'), 'Teilnehmer nicht gefunden!', '', 'error');
                return;
            }
            const player1Type = participant1.isExternal ? 'external' : 'member';
            const player2Type = participant2.isExternal ? 'external' : 'member';
            try {
                await apiClient.post(`/tournament/pairing/${this.currentClub}/${this.selectedDate}/${this.selectedViewClass}`, {
                    player1Type: player1Type,
                    player1Id: this.newPairing.player1Id,
                    player2Type: player2Type,
                    player2Id: this.newPairing.player2Id,
                    seeded: this.newPairing.seeded
                });
                // Reset form
                this.newPairing = {
                    player1Id: null,
                    player2Id: null,
                    seeded: false
                };
                await this.loadPairings();
            } catch (error) {
                console.error('Fehler beim Hinzufügen der Paarung:', error);
                const message = safeErrorMessage(error, 'Fehler beim Hinzufügen der Paarung.');
                await this.showInfo(this.$t('messages.error'), message, '', 'error');
            }
        },

        async removePairing(pairing) {
            try {
                await apiClient.delete(`/tournament/pairing/${this.currentClub}/${this.selectedDate}/${pairing.id}`);
                await this.loadPairings();
            } catch (error) {
                console.error('Fehler beim Löschen der Paarung:', error);
                const message = safeErrorMessage(error, 'Fehler beim Löschen der Paarung.');
                await this.showInfo(this.$t('messages.error'), message, '', 'error');
            }
        },

        async createRandomPairings() {
            const participants = this.getParticipantsForClass(this.selectedViewClass);
            if (participants.length < 2) {
                await this.showInfo(this.$t('messages.error'), 'Mindestens 2 Teilnehmer erforderlich für Paarungen!', '', 'error');
                return;
            }
            
            // Teile Teilnehmer in gesetzte und nicht gesetzte
            const seeded = participants.filter(p => p.seeded);
            const unseeded = participants.filter(p => !p.seeded);
            
            // Prüfe: Wenn mehr gesetzte als nicht gesetzte Spieler, Fehler
            if (seeded.length > unseeded.length) {
                await this.showInfo(this.$t('messages.error'), this.$t('tournaments.errorMoreSeededThanUnseeded'), '', 'error');
                return;
            }
            
            // Bestätigung vor dem Löschen bestehender Paarungen
            if (this.pairings.length > 0) {
                const confirmed = await this.showConfirm(
                    this.$t('messages.confirm'),
                    'Bestehende Paarungen werden gelöscht. Fortfahren?',
                    '',
                    'warning'
                );
                if (!confirmed) {
                    return;
                }
            }
            
            // Lösche alle bestehenden Paarungen
            for (const pairing of this.pairings) {
                try {
                    await apiClient.delete(`/tournament/pairing/${this.currentClub}/${this.selectedDate}/${pairing.id}`);
                } catch (error) {
                    console.error('Fehler beim Löschen der Paarung:', error);
                }
            }
            
            // Erstelle zufällige Paarungen
            const shuffledSeeded = [...seeded].sort(() => Math.random() - 0.5);
            const shuffledUnseeded = [...unseeded].sort(() => Math.random() - 0.5);
            
            const newPairings = [];
            let unseededIndex = 0;
            
            // Paare jeden gesetzten Spieler mit einem ungesetzten
            for (const seededPlayer of shuffledSeeded) {
                if (unseededIndex < shuffledUnseeded.length) {
                    newPairings.push({
                        player1: seededPlayer,
                        player2: shuffledUnseeded[unseededIndex]
                    });
                    unseededIndex++;
                }
            }
            
            // Wenn noch ungesetzte Spieler übrig sind, paare sie untereinander
            const remainingUnseeded = shuffledUnseeded.slice(unseededIndex);
            if (remainingUnseeded.length >= 2) {
                // Paare die übrigen ungesetzten untereinander
                for (let i = 0; i < remainingUnseeded.length - 1; i += 2) {
                    newPairings.push({
                        player1: remainingUnseeded[i],
                        player2: remainingUnseeded[i + 1]
                    });
                }
            }
            // Wenn ungerade Anzahl ungesetzter übrig, bleibt einer allein (wird nicht gepaart)
            
            // Erstelle die Paarungen im Backend
            let successCount = 0;
            let errorCount = 0;
            for (const pairing of newPairings) {
                try {
                    const participant1 = pairing.player1;
                    const participant2 = pairing.player2;
                    const player1Type = participant1.isExternal ? 'external' : 'member';
                    const player2Type = participant2.isExternal ? 'external' : 'member';
                    
                    await apiClient.post(`/tournament/pairing/${this.currentClub}/${this.selectedDate}/${this.selectedViewClass}`, {
                        player1Type: player1Type,
                        player1Id: participant1.id,
                        player2Type: player2Type,
                        player2Id: participant2.id,
                        seeded: false // Paarungen sind nicht gesetzt, nur einzelne Spieler können gesetzt sein
                    });
                    successCount++;
                } catch (error) {
                    console.error('Fehler beim Erstellen der Paarung:', error);
                    errorCount++;
                }
            }
            
            // Lade Pairings neu
            await this.loadPairings();
            
            if (errorCount > 0) {
                await this.showInfo(this.$t('messages.warning'), `${successCount} Paarungen erstellt, ${errorCount} Fehler.`, '', 'warning');
            } else {
                await this.showInfo(this.$t('messages.success'), this.$t('tournaments.randomPairingsCreated'), '', 'success');
            }
        },

        async updatePairingSeeded(pairing, event) {
            const seeded = event.target.checked;
            try {
                await apiClient.put(`/tournament/pairing/${this.currentClub}/${this.selectedDate}/${pairing.id}`, {
                    seeded: seeded
                });
                pairing.seeded = seeded;
            } catch (error) {
                console.error('Fehler beim Aktualisieren des Gesetzt-Status:', error);
                // Bei Fehler: Lade Daten neu
                await this.loadPairings();
            }
        },

        getPairingPlayerName(pairing, playerNumber) {
            if (playerNumber === 1) {
                if (pairing.member1 && pairing.member1.member) {
                    return `${pairing.member1.member.firstName} ${pairing.member1.member.lastName}`;
                } else if (pairing.external1) {
                    return `${pairing.external1.firstName} ${pairing.external1.lastName}`;
                }
            } else if (playerNumber === 2) {
                if (pairing.member2 && pairing.member2.member) {
                    return `${pairing.member2.member.firstName} ${pairing.member2.member.lastName}`;
                } else if (pairing.external2) {
                    return `${pairing.external2.firstName} ${pairing.external2.lastName}`;
                }
            }
            return this.$t('tournaments.unknown');
        },

        getMatchDisplayText(player1Id, player2Id, groupId) {
            const liveResult = this.getMatchLiveResult(player1Id, player2Id, groupId);
            if (!liveResult) return '-';
            
            // Zeige Satzergebnis (z.B. 1:0, 2:1, 0:2)
            return `${liveResult.sets1}:${liveResult.sets2}`;
        },

        getMatchCellClasses(player1Id, player2Id, groupId) {
            const liveResult = this.getMatchLiveResult(player1Id, player2Id, groupId);
            if (!liveResult) return ['no-match'];
            
            const classes = ['match-result'];
            
            if (liveResult.isFinished) {
                // Spiel beendet: Dunkle Farben
                if (liveResult.player1Won) {
                    classes.push('match-finished-win');
                } else if (liveResult.player2Won) {
                    classes.push('match-finished-loss');
                } else {
                    classes.push('match-finished-tie');
                }
            } else {
                // Spiel läuft: Helle Farben
                if (liveResult.player1Won) {
                    classes.push('match-live-win');
                } else if (liveResult.player2Won) {
                    classes.push('match-live-loss');
                } else {
                    classes.push('match-live-tie');
                }
            }
            
            return classes;
        },

        getLivePosition(playerId, groupId) {
            // Berechne Live-Punkte für alle Spieler in der Gruppe
            const groupPlayers = this.groupRankings[groupId] || [];
            const liveStats = groupPlayers.map(player => {
                let livePoints = player.points || 0;
                let liveSetsWon = player.setsWon || 0;
                let liveSetsLost = player.setsLost || 0;
                
                // Füge Live-Punkte aus begonnenen (aber nicht abgeschlossenen) Spielen hinzu
                const playerMatches = this.matches.filter(m => 
                    m.round === 'group' && 
                    m.groupId === groupId &&
                    (m.player1.id === player.id || m.player2.id === player.id) &&
                    !m.isFinished && // Nur begonnene, nicht abgeschlossene Spiele
                    m.tournamentResults && m.tournamentResults.length > 0 // Mit Satz-Ergebnissen
                );
                
                playerMatches.forEach(match => {
                    const isPlayer1 = match.player1.id === player.id;
                    const results = match.tournamentResults || [];
                    
                    if (results.length > 0) {
                        let setsWon = 0, setsLost = 0;
                        results.forEach(result => {
                            if (isPlayer1) {
                                if (result.pointsPlayer1 > result.pointsPlayer2) setsWon++;
                                else if (result.pointsPlayer2 > result.pointsPlayer1) setsLost++;
                            } else {
                                if (result.pointsPlayer2 > result.pointsPlayer1) setsWon++;
                                else if (result.pointsPlayer1 > result.pointsPlayer2) setsLost++;
                            }
                        });
                        
                        // Füge Live-Punkte hinzu (basierend auf aktuellen Sätzen)
                        if (setsWon > setsLost) {
                            livePoints += 1;
                        } else if (setsLost > setsWon) {
                            livePoints -= 1;
                        }
                        
                        // Füge Live-Sätze hinzu
                        if (isPlayer1) {
                            liveSetsWon += results.reduce((sum, r) => sum + r.pointsPlayer1, 0);
                            liveSetsLost += results.reduce((sum, r) => sum + r.pointsPlayer2, 0);
                        } else {
                            liveSetsWon += results.reduce((sum, r) => sum + r.pointsPlayer2, 0);
                            liveSetsLost += results.reduce((sum, r) => sum + r.pointsPlayer1, 0);
                        }
                    }
                });
                
                return {
                    id: player.id,
                    name: player.name,
                    livePoints: livePoints,
                    liveSetsWon: liveSetsWon,
                    liveSetsLost: liveSetsLost,
                    liveSetDiff: liveSetsWon - liveSetsLost
                };
            });
            
            // Sortiere nach Live-Punkten (absteigend), dann nach Satzverhältnis
            liveStats.sort((a, b) => {
                if (b.livePoints !== a.livePoints) return b.livePoints - a.livePoints;
                if (b.liveSetDiff !== a.liveSetDiff) return b.liveSetDiff - a.liveSetDiff;
                if (b.liveSetsWon !== a.liveSetsWon) return b.liveSetsWon - a.liveSetsWon;
                return a.name.localeCompare(b.name);
            });
            
            // Weise Positionen zu, wobei Spieler mit identischen Werten den gleichen Platz bekommen
            let currentPosition = 1;
            const positions = liveStats.map((p, i) => {
                // Wenn nicht der erste Spieler und die Werte identisch sind, verwende die gleiche Position
                if (i > 0) {
                    const prev = liveStats[i - 1];
                    if (prev.livePoints === p.livePoints && 
                        prev.liveSetDiff === p.liveSetDiff && 
                        prev.liveSetsWon === p.liveSetsWon) {
                        // Gleicher Platz wie Vorgänger
                        return currentPosition;
                    }
                }
                // Neuer Platz
                currentPosition = i + 1;
                return currentPosition;
            });
            
            // Finde Position des Spielers
            const playerIndex = liveStats.findIndex(p => p.id === playerId);
            return playerIndex >= 0 ? positions[playerIndex] : liveStats.length + 1;
        }
    }
};
</script>

<style scoped>
.tournaments-view {
    padding: 1rem;
}

.new-tournament {
    margin-top: 1rem;
    display: flex;
    gap: 1rem;
    align-items: flex-end;
    flex-wrap: wrap;
}

.new-tournament label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.new-tournament input[type="text"],
.new-tournament input[type="date"],
.new-tournament input[type="number"] {
    padding: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 1em;
}

.new-tournament input[type="text"] {
    min-width: 200px;
}

.tournament-info {
    margin-bottom: 1rem;
    display: flex;
    gap: 1rem;
    align-items: flex-end;
    flex-wrap: wrap;
}

.tournament-info label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.tournament-info input[type="text"],
.tournament-info input[type="date"],
.tournament-info input[type="number"] {
    padding: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 1em;
}

.tournament-info input[type="text"] {
    min-width: 200px;
}

/* Tab-System */
.tournament-tabs {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 1rem;
    border-bottom: 2px solid #dee2e6;
}

.tab-button {
    padding: 0.75rem 1.5rem;
    background: none;
    border: none;
    border-bottom: 3px solid transparent;
    cursor: pointer;
    font-size: 1em;
    color: #6c757d;
    transition: all 0.2s;
    margin-bottom: -2px;
}

.tab-button:hover {
    color: #495057;
    background-color: #f8f9fa;
}

.tab-button.active {
    color: #28a745;
    border-bottom-color: #28a745;
    font-weight: 500;
}

.tab-button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.tab-content {
    padding: 1rem 0;
    display: block !important; /* Überschreibe globale display: none Regel */
}

.participants,
.group-controls,
.groups-overview,
.ko-round,
.ko-start {
    margin-top: 1.5rem;
}

.class-section {
    margin-top: 2rem;
    margin-bottom: 1rem;
}

.class-section:first-child {
    margin-top: 0;
}

.class-header {
    font-size: 1.2em;
    font-weight: bold;
    color: #333;
    margin-bottom: 0.5rem;
    padding-bottom: 0.5rem;
    border-bottom: 2px solid #ddd;
}

.group-table {
    margin-bottom: 2rem;
}

table {
    width: 100%;
    border-collapse: collapse;
}

th,
td {
    padding: 0.5em;
    border: 1px solid #ccc;
    text-align: left;
}

button {
    margin-left: 0.5em;
}

.match-cell.diagonal-cell {
    position: relative;
    padding: 0;
}

.diagonal {
    background-color: #bbb;
    color: #bbb;
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
}

.match-result {
    font-weight: bold;
    padding: 2px 4px;
    border-radius: 3px;
}

.match-result.win {
    background-color: #d4edda;
    color: #155724;
}

.match-result.loss {
    background-color: #f8d7da;
    color: #721c24;
}

/* Live-Ergebnisse während des Spiels */
.match-live-win {
    background-color: #d1eca1; /* Hellgrün */
    color: #0c5460;
    font-weight: bold;
}

.match-live-loss {
    background-color: #ffeaa7; /* Orange */
    color: #d63031;
    font-weight: bold;
}

.match-live-tie {
    background-color: #f8f9fa; /* Neutral */
    color: #6c757d;
    font-weight: bold;
}

/* Beendete Spiele */
.match-finished-win {
    background-color: #28a745; /* Dunkelgrün */
    color: white;
    font-weight: bold;
}

.match-finished-loss {
    background-color: #fd7e14; /* Dunkelorange */
    color: white;
    font-weight: bold;
}

.match-finished-tie {
    background-color: #6c757d; /* Grau */
    color: white;
    font-weight: bold;
}

/* Kollabierbare Teilnehmerliste */
.participants-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    cursor: pointer;
    padding: 0.5rem 0;
    border-bottom: 1px solid #dee2e6;
    margin-bottom: 1rem;
}

.participants-header:hover {
    background-color: #f8f9fa;
    border-radius: 4px;
}

.participants-header h4 {
    margin: 0;
    color: #495057;
}

.collapse-icon {
    font-size: 0.8em;
    color: #6c757d;
    transition: transform 0.3s ease;
    user-select: none;
}

.collapse-icon.expanded {
    transform: rotate(180deg);
}

.participants-content {
    animation: slideDown 0.3s ease;
}

@keyframes slideDown {
    from {
        opacity: 0;
        max-height: 0;
    }
    to {
        opacity: 1;
        max-height: 1000px;
    }
}

.participants-table-container {
    border: 1px solid #dee2e6;
    border-radius: 4px;
    overflow: hidden;
    display: inline-block;
    width: auto;
}

.participants-table {
    width: auto;
    border-collapse: collapse;
    font-size: 0.9em;
}

.participants-table-header {
    margin: 0;
    border-bottom: none;
}

.participants-table-header th {
    font-weight: bold;
    font-size: 0.85em;
    color: #495057;
    padding: 0.15rem 0.5rem;
    text-align: left;
    white-space: nowrap;
    background-color: white;
}

.participants-table-body-wrapper {
    max-height: 400px;
    overflow-y: auto;
    overflow-x: auto;
    margin: 0;
}

.participants-table-body td {
    padding: 0.1rem 0.5rem;
    white-space: nowrap;
}

/* Synchronisiere Spaltenbreiten zwischen Header und Body */
.participants-table-header .participant-seeded-cell,
.participants-table-body .participant-seeded-cell {
    width: 60px;
}

.participants-table-header .participant-name,
.participants-table-body .participant-name {
    min-width: 150px;
}

.participants-table-header .participant-club-cell,
.participants-table-body .participant-club-cell {
    min-width: 120px;
}

.participants-table-header .participant-class-cell,
.participants-table-body .participant-class-cell {
    min-width: 120px;
}

.participants-table-header .participant-group-cell,
.participants-table-body .participant-group-cell {
    width: 80px;
}

.participants-table-header .participant-action-cell,
.participants-table-body .participant-action-cell {
    width: 50px;
}

.participant-item {
    line-height: 1.2;
}

.participant-seeded-cell {
    white-space: nowrap;
}

.seeded-checkbox-label {
    display: flex;
    align-items: center;
    cursor: pointer;
    user-select: none;
}

.seeded-checkbox-label input[type="checkbox"] {
    cursor: pointer;
    margin: 0;
}

.participant-name {
    white-space: nowrap;
}

.participant-group-cell {
    white-space: nowrap;
}

.participant-action-cell {
    white-space: nowrap;
}

.participant-class-cell {
    white-space: nowrap;
}

.class-select-small {
    font-size: 0.85em;
    padding: 2px 4px;
    min-width: 100px;
    border: 1px solid #ccc;
    border-radius: 3px;
}

.group-select-small {
    font-size: 0.85em;
    padding: 2px 4px;
    width: 50px;
    border: 1px solid #ccc;
    border-radius: 3px;
}

.trash-btn-small {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.9em;
    padding: 0;
    opacity: 0.7;
    transition: opacity 0.2s;
}

.trash-btn-small:hover {
    opacity: 1;
}

.add-participant {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid #dee2e6;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.add-participant-section {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.add-participant-section h5 {
    margin: 0;
    font-size: 0.9em;
    color: #495057;
}

.add-participant-row {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
    width: fit-content;
}

.external-input {
    padding: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 0.9em;
    min-width: 120px;
}

.member-select {
    min-width: 200px;
    max-width: 300px;
    padding: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
}

.btn-add {
    padding: 0.4rem 0.8rem;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.9em;
    white-space: nowrap;
}

.btn-add:hover {
    background-color: #0056b3;
}

.training-btn {
    background-color: #28a745;
    color: white;
    border: none;
    padding: 0.5rem 1rem;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.9em;
    transition: background-color 0.2s ease;
}

.training-btn:hover {
    background-color: #218838;
}

.training-btn:active {
    background-color: #1e7e34;
}

.tournament-classes {
    margin-top: 1.5rem;
}

.classes-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    cursor: pointer;
    padding: 0.5rem 0;
    border-bottom: 1px solid #dee2e6;
    margin-bottom: 1rem;
}

.classes-header:hover {
    background-color: #f8f9fa;
    border-radius: 4px;
}

.classes-header h4 {
    margin: 0;
    color: #495057;
}

.classes-content {
    animation: slideDown 0.3s ease;
}

.classes-list {
    margin-bottom: 1rem;
}

.class-item {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 0.5rem;
}

.class-name-label {
    flex: 1;
    padding: 0.4rem;
    font-size: 0.9em;
    min-width: 200px;
}

.class-type-badge {
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 12px;
    background-color: #e0e0e0;
    color: #666;
    margin-left: 8px;
}

.class-type-badge.doubles {
    background-color: #4caf50;
    color: white;
}

.class-doubles-checkbox {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: 12px;
    font-size: 14px;
    cursor: pointer;
}

.class-doubles-checkbox input[type="checkbox"] {
    cursor: pointer;
}

.class-gender-select {
    padding: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 0.9em;
    min-width: 100px;
    background-color: white;
}

.class-gender-badge {
    display: inline-block;
    padding: 0.2rem 0.5rem;
    border-radius: 4px;
    font-size: 0.85em;
    margin-left: 0.5rem;
    background-color: #e9ecef;
    color: #495057;
}

.class-gender-badge.gender-male {
    background-color: #cfe2ff;
    color: #084298;
}

.class-gender-badge.gender-female {
    background-color: #f8d7da;
    color: #842029;
}

.class-gender-badge.gender-mixed {
    background-color: #fff3cd;
    color: #664d03;
}

.groups-per-class {
    margin-top: 1rem;
    padding: 1rem;
    background-color: #f8f9fa;
    border-radius: 4px;
}

.groups-per-class h4 {
    margin-top: 0;
    margin-bottom: 0.5rem;
}

.groups-per-class-hint {
    font-size: 0.9em;
    color: #666;
    margin-bottom: 1rem;
    font-style: italic;
}

.class-group-config {
    margin-bottom: 0.75rem;
}

.class-group-label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.95em;
}

.class-group-name {
    min-width: 150px;
    font-weight: 500;
}

.class-group-type {
    font-size: 0.85em;
    color: #666;
    font-style: italic;
}

.class-group-type.doubles {
    color: #4caf50;
    font-weight: 500;
}

.class-group-input {
    width: 80px;
    padding: 0.25rem 0.5rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    text-align: center;
}

.class-group-unit {
    color: #666;
    font-size: 0.9em;
}

.class-name-input {
    flex: 1;
    padding: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 0.9em;
    min-width: 200px;
}

.btn-edit-small,
.btn-save-small,
.btn-cancel-small {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.9em;
    padding: 0;
    opacity: 0.7;
    transition: opacity 0.2s;
    margin-left: 0.25rem;
}

.btn-edit-small:hover,
.btn-save-small:hover,
.btn-cancel-small:hover {
    opacity: 1;
}

.btn-save-small {
    color: #28a745;
}

.btn-cancel-small {
    color: #dc3545;
}

.add-class {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    padding-top: 0.5rem;
    border-top: 1px solid #dee2e6;
}

.no-match {
    color: #ccc;
}

.participants-class-section {
    margin-bottom: 2rem;
}

.participants-class-header {
    margin: 1rem 0 0.5rem 0;
    padding: 0.5rem;
    background-color: #e9ecef;
    border-radius: 4px;
    font-size: 1.1em;
    font-weight: 600;
}

.class-type-badge-small {
    font-size: 0.85em;
    font-weight: normal;
    color: #666;
}

.class-type-badge-small.doubles {
    color: #4caf50;
    font-weight: 500;
}

.class-selection-section {
    margin: 1.5rem 0;
    padding: 1rem;
    background-color: #f8f9fa;
    border-radius: 4px;
    border: 1px solid #dee2e6;
}

.participants-class-filter {
    margin-bottom: 0;
    padding: 0;
}

.participants-class-filter label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 500;
}

.class-filter-select {
    padding: 0.5rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 0.95em;
    min-width: 200px;
}

.class-assignment-info {
    margin-bottom: 1rem;
    padding: 0.5rem 0.75rem;
    background-color: #f1f3f5;
    border-radius: 4px;
    display: flex;
    gap: 0.5rem;
    align-items: center;
    font-size: 0.9em;
}

.class-assignment-info.no-selection {
    border: 1px dashed #ff6b6b;
    background-color: #fff5f5;
    color: #ff6b6b;
}

.class-assignment-info .label {
    font-weight: 600;
}

.group-table table {
    font-size: 0.9em;
}

.group-table th,
.group-table td {
    padding: 0.3em 0.5em;
    text-align: center;
}

.group-table th:first-child,
.group-table td:first-child {
    text-align: left;
    font-weight: bold;
}

.group-table th:nth-child(3),
.group-table td:nth-child(3) {
    text-align: left;
    min-width: 120px;
}

.seeded-star {
    color: #ffd700;
    margin-right: 0.25em;
}

.match-cell.clickable {
    cursor: pointer;
    transition: all 0.2s ease;
    position: relative;
}

.match-cell.clickable:hover {
    background-color: #f8f9fa;
    transform: scale(1.02);
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.match-highlight {
    background-color: #fff3cd !important;
    border: 2px solid #ffc107 !important;
    animation: highlight-pulse 0.5s ease-in-out;
}

@keyframes highlight-pulse {
    0% { transform: scale(1); }
    50% { transform: scale(1.02); }
    100% { transform: scale(1); }
}

/* Container für neue Satzeingabe - inline */
.new-set-line {
    display: inline-block;
    margin-left: 0.5rem;
}

/* Eingabefeld für Sätze - nur so breit wie nötig für die Eingabe */
.inline-input {
    width: 5ch; /* 5 Zeichen breit */
    padding: calc(0.2rem + 2px) calc(0.4rem + 2px);
    font-family: inherit;
    text-align: center;
    border: 1px solid #dee2e6;
    border-radius: 3px;
    box-sizing: border-box;
    line-height: 1.5;
    font-size: inherit;
}

/* Klickbare Ergebnis-Texte */
.result-text.clickable {
    cursor: pointer;
    padding: 0.2rem 0.4rem;
    border-radius: 3px;
    border: 1px solid transparent;
    transition: all 0.2s ease;
    line-height: 1.5;
    display: inline-block;
}

.result-text.clickable:hover {
    background-color: #f8f9fa;
    border-color: #dee2e6;
}

/* Aktive Begegnung hervorheben */
tr.active-match {
    background-color: #fff3cd !important;
    border-left: 3px solid #ffc107;
    cursor: pointer;
}

tr.active-match:hover {
    background-color: #ffe69c !important;
}

/* Tabellenzeilen klickbar machen */
tbody tr {
    cursor: pointer;
    transition: background-color 0.2s ease;
}

tbody tr:hover:not(.active-match) {
    background-color: #f8f9fa;
}

/* Aktive Gruppentabellen-Zellen hervorheben */
.match-cell.active-group-cell {
    background-color: #fff3cd !important;
    border: 2px solid #ffc107 !important;
    font-weight: bold;
}

/* Abgeschlossene Spiele in grauer Schrift */
.match-finished {
    color: #626262 !important;
    background-color: #e9ecef !important;
}

.match-finished td {
    color: #828a91 !important;
}

/* Laufende Spiele farblich hervorheben - nur wenn nicht abgeschlossen (orange wie vorher) */
.match-live:not(.match-finished) {
    background-color: #fff3cd !important; /* helles Orange/Gelb */
    border-left: 4px solid #ff9800 !important; /* oranger Rand */
}

.match-live:not(.match-finished) td {
    color: #8a4f28; /* dunkles Orange/Braun für Lesbarkeit */
    font-weight: 500;
}

/* Button für "Läuft gerade" */
.btn-live {
    margin-left: 0.5rem;
    padding: 0.25rem 0.5rem;
    font-size: 0.9em;
    background-color: #f8f9fa;
    border: 1px solid #dee2e6;
    border-radius: 3px;
    cursor: pointer;
    transition: all 0.2s ease;
}

.btn-live:hover {
    background-color: #e9ecef;
    border-color: #adb5bd;
}

.btn-live.active {
    background-color: #28a745;
    border-color: #28a745;
    color: white;
}

.btn-live.active:hover {
    background-color: #218838;
    border-color: #1e7e34;
}

/* Button für "Korrigieren" - weißer Hintergrund mit grüner Schrift */
.match-finished td .btn-correct,
.btn-correct {
    background: white !important;
    background-color: white !important;
    background-image: none !important;
    color: #4CAF50 !important;
    border: 1px solid #4CAF50 !important;
    padding: 0.25rem 0.5rem;
    font-size: 0.9em;
    border-radius: 3px;
    cursor: pointer;
    transition: all 0.2s ease;
    box-shadow: none !important;
    min-height: auto !important;
}

.match-finished td .btn-correct::before,
.btn-correct::before {
    display: none !important;
}

.match-finished td .btn-correct:hover,
.btn-correct:hover {
    background: #f8f9fa !important;
    background-color: #f8f9fa !important;
    background-image: none !important;
    border-color: #45a049 !important;
    color: #45a049 !important;
    transform: none !important;
}
.pairings-section {
    margin-top: 2rem;
    padding: 1rem;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    background-color: #f8f9fa;
}

.pairings-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    cursor: pointer;
    padding: 0.5rem 0;
}

.pairings-header h4 {
    margin: 0;
}

.pairings-content {
    margin-top: 1rem;
}

.add-pairing {
    margin-bottom: 1.5rem;
    padding: 1rem;
    background-color: white;
    border-radius: 4px;
    border: 1px solid #dee2e6;
}

.add-pairing h5 {
    margin: 0 0 0.75rem 0;
    font-size: 0.9em;
    color: #495057;
}

.pairing-form {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
    width: fit-content;
}

.pairing-player-type {
    padding: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 0.9em;
    min-width: 100px;
}

.pairing-player-select {
    min-width: 180px;
    max-width: 250px;
    padding: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
}

.pairing-separator {
    font-weight: bold;
    font-size: 1.2em;
    color: #495057;
}

.pairing-seeded-label {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.9em;
    white-space: nowrap;
}

.pairings-list {
    margin-top: 1rem;
}

.pairings-table {
    width: 100%;
    border-collapse: collapse;
    background-color: white;
    border-radius: 4px;
    overflow: hidden;
}

.pairings-table thead {
    background-color: #28a745;
    color: white;
}

.pairings-table th,
.pairings-table td {
    padding: 0.75rem;
    text-align: left;
    border-bottom: 1px solid #dee2e6;
}

.pairings-table tbody tr:hover {
    background-color: #f8f9fa;
}

.random-pairing-section {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid #dee2e6;
}

.btn-random-pairings {
    padding: 0.5rem 1rem;
    background-color: #28a745;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.95em;
    font-weight: 500;
}

.btn-random-pairings:hover {
    background-color: #218838;
}

.btn-random-pairings:disabled {
    background-color: #6c757d;
    cursor: not-allowed;
}

.random-pairing-section {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid #dee2e6;
}

.btn-random-pairings {
    padding: 0.5rem 1rem;
    background-color: #28a745;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.95em;
    font-weight: 500;
}

.btn-random-pairings:hover {
    background-color: #218838;
}

.btn-random-pairings:disabled {
    background-color: #6c757d;
    cursor: not-allowed;
}
</style>
