<template>
    <div class="schedule-view">
        <ScheduleLayoutShell
            ref="scheduleShell"
            :selected-season-id="selectedSeasonId"
            :title="friendlyOnly ? 'Freundschaftsspiele' : $t('schedule.title')"
            :subtitle="friendlyOnly ? 'Interne Spiele ohne Click-TT-Verbindung verwalten.' : $t('schedule.subtitle')"
            :show-gallery-button="Boolean(playerSelectionDialog.match)"
            :gallery-loading="galleryLoading"
            :filtered-schedule-teams-count="filteredScheduleTeams.length"
            :teams-count="teams.length"
            :matches-count="matches.length"
            :completed-matches-count="completedMatchesCount"
            :pending-matches-count="pendingMatchesCount"
            :next-scheduled-match-label="nextScheduledMatchLabel"
            :team-search-query="teamSearchQuery"
            :overall-schedule-label="$t('schedule.overallSchedule')"
            :adult-schedule-label="$t('schedule.adultSchedule')"
            :selected-league="selectedLeague"
            :filtered-schedule-teams="filteredScheduleTeams"
            :selected-team="selectedTeam"
            :workspace-description="workspaceDescription"
            :active-tab="activeTab"
            :table-count="leagueTable.length"
            :fetching-team-data="fetchingTeamData"
            :fetching-table="fetchingTable"
            :show-friendly-actions="friendlyOnly"
            :show-schedule-actions="!friendlyOnly"
            :show-sidebar="!friendlyOnly"
            :show-table-tab="!friendlyOnly"
            @update:selected-season-id="selectedSeasonId = $event"
            @season-change="onSeasonChange"
            @open-import-modal="openImportModal"
            @open-friendly-match-modal="openFriendlyMatchDialog"
            @open-gallery-dialog="openGalleryDialog"
            @update:team-search-query="teamSearchQuery = $event"
            @load-all-matches="loadAllMatches"
            @load-adult-matches="loadAdultMatches"
            @load-team="loadMatchesForTeam"
            @fetch-team-data-manually="fetchTeamDataManually"
            @generate-pdf="generatePDF"
            @fetch-table="fetchTableFromMyTischtennis"
            @update:active-tab="activeTab = $event"
        >
            <template #schedule-panel>
                <div v-if="friendlyOnly" class="friendly-invitations-card">
                    <div class="friendly-invitations-header">
                        <strong>Vereinsuebergreifende Einladungen</strong>
                        <button type="button" class="btn-secondary" @click="openFriendlyInvitationDialog">Verein einladen</button>
                    </div>
                    <div class="friendly-invitations-grid">
                        <div>
                            <h4>Eingehend ({{ incomingFriendlyInvitations.length }})</h4>
                            <ul v-if="incomingFriendlyInvitations.length" class="friendly-invitation-list">
                                <li v-for="invitation in incomingFriendlyInvitations" :key="`incoming-${invitation.id}`">
                                    <div class="friendly-invitation-main">
                                        <span><strong>{{ getClubNameById(invitation.fromClubId) }}</strong> · {{ invitation.proposedDate }} {{ invitation.proposedStartTime || '' }}</span>
                                        <small>{{ invitation.proposedMatchName }}</small>
                                    </div>
                                    <div class="friendly-invitation-actions">
                                        <button type="button" class="btn-save" @click="acceptFriendlyInvitation(invitation.id)">Annehmen</button>
                                        <button type="button" class="btn-cancel" @click="declineFriendlyInvitation(invitation.id)">Ablehnen</button>
                                    </div>
                                </li>
                            </ul>
                            <p v-else class="friendly-invitation-empty">Keine eingehenden Einladungen.</p>
                        </div>
                        <div>
                            <h4>Ausgehend ({{ outgoingFriendlyInvitations.length }})</h4>
                            <ul v-if="outgoingFriendlyInvitations.length" class="friendly-invitation-list">
                                <li v-for="invitation in outgoingFriendlyInvitations" :key="`outgoing-${invitation.id}`">
                                    <div class="friendly-invitation-main">
                                        <span><strong>{{ getClubNameById(invitation.toClubId) }}</strong> · {{ invitation.proposedDate }} {{ invitation.proposedStartTime || '' }}</span>
                                        <small>{{ invitation.proposedMatchName }}</small>
                                    </div>
                                    <span class="friendly-invitation-status">{{ invitation.status || 'pending' }}</span>
                                </li>
                            </ul>
                            <p v-else class="friendly-invitation-empty">Keine ausgehenden Einladungen.</p>
                        </div>
                    </div>
                </div>
                <div v-if="selectedTeam" class="league-match-scope-card">
                    <div class="league-match-scope-header">
                        <strong>{{ $t('schedule.matchOverviewTitle') }}</strong>
                        <span>{{ $t('schedule.matchOverviewDescription') }}</span>
                    </div>
                    <div class="league-match-scope-controls">
                        <button
                            type="button"
                            class="team-filter-chip"
                            :class="{ active: leagueMatchScope === 'own' }"
                            @click="setLeagueMatchScope('own')"
                        >
                            {{ $t('schedule.ownTeamMatches') }}
                        </button>
                        <button
                            type="button"
                            class="team-filter-chip"
                            :class="{ active: leagueMatchScope === 'all' }"
                            @click="setLeagueMatchScope('all')"
                        >
                            {{ $t('schedule.allLeagueMatches') }}
                        </button>
                        <button
                            type="button"
                            class="team-filter-chip"
                            :class="{ active: leagueMatchScope === 'other' }"
                            @click="setLeagueMatchScope('other')"
                        >
                            {{ $t('schedule.otherTeamMatches') }}
                        </button>
                        <select
                            v-if="leagueMatchScope === 'other'"
                            v-model="selectedComparisonTeamName"
                            class="league-match-scope-select"
                            @change="applyLeagueMatchScope"
                        >
                            <option value="">{{ $t('schedule.selectOtherTeam') }}</option>
                            <option v-for="teamName in leagueTeamOptions" :key="teamName" :value="teamName">
                                {{ teamName }}
                            </option>
                        </select>
                    </div>
                </div>
                <div v-if="matches.length > 0">
                    <div class="schedule-table-wrapper">
                        <table id="schedule-table">
                            <thead>
                                <tr>
                                    <th>{{ $t('schedule.locationInfo') }}</th>
                                    <th>{{ $t('schedule.date') }}</th>
                                    <th>{{ $t('schedule.time') }}</th>
                                    <th>{{ $t('schedule.homeTeam') }}</th>
                                    <th>{{ $t('schedule.guestTeam') }}</th>
                                    <th v-if="!friendlyOnly">{{ $t('schedule.result') }}</th>
                                    <th
                                        v-if="selectedLeague === $t('schedule.overallSchedule') || selectedLeague === $t('schedule.adultSchedule')">
                                        {{ $t('schedule.ageClass') }}</th>
                                    <th v-if="!friendlyOnly">{{ $t('schedule.code') }}</th>
                                    <th>{{ friendlyOnly ? 'Aktionen' : $t('schedule.homePin') }}</th>
                                    <th v-if="!friendlyOnly">{{ $t('schedule.guestPin') }}</th>
                                    <th v-if="friendlyOnly">{{ $t('schedule.homeGame') || 'Heimspiel' }}</th>
                                    <th v-if="friendlyOnly">{{ $t('schedule.participants') || 'Teilnehmer' }}</th>
                                    <th v-if="friendlyOnly">&nbsp;</th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr v-for="match in matches" :key="match.id" 
                                    @click="friendlyOnly ? openFriendlyMatchDialog(match) : openPlayerSelectionDialog(match)"
                                    :class="getRowClass(match.date)"
                                    style="cursor: pointer;">
                                    <td class="location-info-cell">
                                        <button
                                            v-if="match.location"
                                            type="button"
                                            class="location-info-button"
                                            :title="$t('schedule.showLocation')"
                                            @click.stop="openLocationDialog(match)"
                                        >
                                            📍
                                        </button>
                                        <span v-else class="no-data">-</span>
                                    </td>
                                    <td>{{ formatDate(match.date) }}</td>
                                    <td>{{ match.time ? match.time.toString().slice(0, 5) + ' ' + $t('common.time') : 'N/A' }}</td>
                                    <td :class="{ 'highlighted-club': isClubHighlighted(match.homeTeam?.name) }">
                                        {{ match.homeTeam?.name || 'N/A' }}
                                    </td>
                                    <td :class="{ 'highlighted-club': isClubHighlighted(match.guestTeam?.name) }">
                                        {{ match.guestTeam?.name || 'N/A' }}
                                    </td>
                                    <td v-if="!friendlyOnly" class="result-cell" :class="getResultClass(match)">
                                        <span v-if="match.isCompleted" class="result-score">
                                            {{ match.homeMatchPoints }}:{{ match.guestMatchPoints }}
                                        </span>
                                        <span v-else class="result-pending">—</span>
                                    </td>
                                    <td
                                        v-if="selectedLeague === $t('schedule.overallSchedule') || selectedLeague === $t('schedule.adultSchedule')">
                                        {{ match.leagueDetails?.name || 'N/A' }}</td>
                                    <td v-if="!friendlyOnly" class="code-cell match-report-cell" @click.stop="openMatchReport(match)">
                                        <span v-if="match.code && selectedLeague && selectedLeague !== ''">
                                            <button @click.stop="openMatchReport(match)" class="nuscore-link"
                                                :title="$t('schedule.openMatchReport')">📊</button>
                                            <span class="code-value clickable" @click.stop="copyToClipboard(match.code, $t('schedule.code'), $event)"
                                                :title="$t('schedule.copyCode') + ': ' + match.code">{{ match.code }}</span>
                                        </span>
                                        <span v-else-if="match.code" class="code-value clickable"
                                            @click.stop="copyToClipboard(match.code, $t('schedule.code'), $event)"
                                            :title="$t('schedule.copyCode') + ': ' + match.code">{{ match.code }}</span>
                                        <span v-else-if="!match.isFriendly" class="no-data">-</span>
                                    </td>
                                    <td v-if="friendlyOnly" class="participants-cell">
                                        <button type="button" class="btn-secondary" @click.stop="openPlayerSelectionDialog(match)">
                                            {{ (Array.isArray(match.playersPlanned) ? match.playersPlanned.length : (match.playersPlanned ? JSON.parse(match.playersPlanned).length : 0)) }} {{ $t('schedule.plannedShort') || 'geplant' }}
                                            ·
                                            {{ (Array.isArray(match.playersReady) ? match.playersReady.length : (match.playersReady ? JSON.parse(match.playersReady).length : 0)) }} {{ $t('schedule.readyShort') || 'bereit' }}
                                        </button>
                                    </td>

                                    <td v-if="friendlyOnly" class="home-away-cell">
                                        <div class="home-away-status">
                                            <span class="status-label">
                                                {{ isOurClubPlayingHome(match) ? $t('schedule.homeLabel') || $t('schedule.homeGame') : $t('schedule.awayLabel') || $t('schedule.away') }}
                                            </span>
                                            <button type="button" class="btn-small" :disabled="isFriendlyMatchReadOnly(match)" @click.stop="toggleHomeAway(match)">
                                                {{ $t('schedule.swapButton') }}
                                            </button>
                                        </div>
                                    </td>

                                    <td class="pin-cell">
                                        <div v-if="match.isFriendly" class="friendly-actions-cell">
                                            <button type="button" class="btn-secondary" @click.stop="openFriendlyResultDialog(match)">{{ isFriendlyMatchReadOnly(match) ? 'Ansehen' : 'Ergebnis' }}</button>
                                            <button type="button" class="btn-secondary" @click.stop="openFriendlyMatchDialog(match)">{{ isFriendlyMatchReadOnly(match) ? 'Details' : 'Bearbeiten' }}</button>
                                        </div>
                                        <span v-else-if="match.homePin" class="pin-value clickable"
                                            @click.stop="copyToClipboard(match.homePin, $t('schedule.homePin'), $event)"
                                            :title="$t('schedule.copyHomePin') + ': ' + match.homePin">{{ match.homePin }}</span>
                                        <span v-else class="no-data">-</span>
                                    </td>
                                    <td v-if="!friendlyOnly" class="pin-cell">
                                        <span v-if="match.guestPin" class="pin-value clickable"
                                            @click.stop="copyToClipboard(match.guestPin, $t('schedule.guestPin'), $event)"
                                            :title="$t('schedule.copyGuestPin') + ': ' + match.guestPin">{{ match.guestPin }}</span>
                                        <span v-else class="no-data">-</span>
                                    </td>
                                    <td v-if="friendlyOnly" class="result-cell" :class="getResultClass(match)">
                                        <span v-if="match.isCompleted" class="result-score">
                                            {{ match.homeMatchPoints }}:{{ match.guestMatchPoints }}
                                        </span>
                                        <span v-else class="result-pending">—</span>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
                <div v-else>
                    <p>{{ $t('schedule.noGames') }}</p>
                </div>
            </template>
            <template #table-panel>
                <div class="table-section">
                    <div class="table-header">
                        <h3>{{ $t('schedule.leagueTable') }}</h3>
                    </div>
                    <div v-if="leagueTable.length > 0">
                        <div class="schedule-table-wrapper">
                            <table id="league-table">
                                <thead>
                                    <tr>
                                        <th>{{ $t('schedule.position') }}</th>
                                        <th>{{ $t('schedule.team') }}</th>
                                        <th>{{ $t('schedule.matches') }}</th>
                                        <th>{{ $t('schedule.sets') }}</th>
                                        <th>{{ $t('schedule.points') }}</th>
                                        <th>{{ $t('schedule.balls') }}</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr v-for="(team, index) in leagueTable" :key="team.teamId" :class="{ 'our-team': isOurTeam(team.teamName) }">
                                        <td>{{ index + 1 }}</td>
                                        <td>{{ team.teamName }}</td>
                                        <td>{{ team.matchPoints }}</td>
                                        <td>{{ team.setsWon }}:{{ team.setsLost }}</td>
                                        <td>{{ team.tablePoints }}</td>
                                        <td>{{ team.pointRatio }}</td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                    <div v-else>
                        <p>{{ $t('schedule.noTableData') }}</p>
                    </div>
                </div>
            </template>
        </ScheduleLayoutShell>

        <!-- Import Modal -->
        <CsvImportDialog v-model="showImportModal" @import="handleCsvImport" @close="closeImportModal" />
    </div>


    <!-- Info Dialog -->
    <InfoDialog v-model="infoDialog.isOpen" :title="infoDialog.title" :message="infoDialog.message"
        :details="infoDialog.details" :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)" />
    
    <!-- Mitglieder-Galerie Dialog -->
    <BaseDialog
        v-model="showGalleryDialog"
        :title="$t('schedule.galleryTitle')"
        size="large"
        :close-on-overlay="true"
        @close="closeGalleryDialog"
    >
        <div class="gallery-dialog-content">
            <div class="gallery-controls">
                <label for="gallery-size">{{ $t('schedule.imageSize') }}:</label>
                <select id="gallery-size" v-model="gallerySize" @change="loadGalleryMembers" :disabled="galleryLoading">
                    <option :value="100">100x100 px</option>
                    <option :value="150">150x150 px</option>
                    <option :value="200">200x200 px</option>
                </select>
            </div>
            <div v-if="galleryLoading" class="gallery-loading">{{ $t('schedule.galleryLoading') }}</div>
            <div v-else-if="galleryMembers.length > 0" class="gallery-members-grid">
                <div 
                    v-for="member in galleryMembers" 
                    :key="member.memberId"
                    class="gallery-member-item"
                    :class="{ 'is-ready': isMemberReady(member.memberId) }"
                    @click="toggleMemberReady(member)"
                >
                    <img 
                        :src="getMemberImageUrl(member.memberId)" 
                        :alt="member.fullName"
                        class="gallery-member-image"
                    />
                    <div class="gallery-member-name">{{ member.fullName }}</div>
                </div>
            </div>
            <div v-else class="gallery-error">
                {{ galleryError || $t('schedule.noMembersWithImages') }}
            </div>
        </div>
    </BaseDialog>

    <!-- Player Selection Dialog -->
    <BaseDialog 
        v-model="playerSelectionDialog.isOpen" 
        :title="`${$t('schedule.playerSelection')} - ${playerSelectionDialog.match?.homeTeam?.name || ''} ${$t('schedule.vs')} ${playerSelectionDialog.match?.guestTeam?.name || ''}`"
        @close="closePlayerSelectionDialog"
        :max-width="800"
    >
        <div v-if="playerSelectionDialog.loading" class="loading-state">
            {{ $t('schedule.loadingMembers') }}
        </div>
        
        <div v-else class="player-selection-content">
            <div class="match-info">
                <p><strong>{{ $t('schedule.date') }}:</strong> {{ formatDate(playerSelectionDialog.match?.date) }}</p>
                <p><strong>{{ $t('schedule.time') }}:</strong> {{ playerSelectionDialog.match?.time ? playerSelectionDialog.match.time.toString().slice(0, 5) + ' ' + $t('common.time') : 'N/A' }}</p>
            </div>
            
            <div class="player-list">
                <table class="player-selection-table">
                    <thead>
                        <tr>
                            <th>{{ $t('schedule.player') }}</th>
                            <th>{{ $t('schedule.ready') }}</th>
                            <th>{{ $t('schedule.planned') }}</th>
                            <th>{{ $t('schedule.played') }}</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr v-for="member in playerSelectionDialog.members" :key="member.id">
                            <td class="player-name">
                                {{ member.firstName }} {{ member.lastName }}
                            </td>
                            <td class="checkbox-cell">
                                <input 
                                    type="checkbox" 
                                    :checked="member.isReady"
                                    :disabled="playerSelectionDialog.readonly"
                                    @change="togglePlayerReady(member)"
                                />
                            </td>
                            <td class="checkbox-cell">
                                <input 
                                    type="checkbox" 
                                    :checked="member.isPlanned"
                                    :disabled="playerSelectionDialog.readonly"
                                    @change="togglePlayerPlanned(member)"
                                />
                            </td>
                            <td class="checkbox-cell">
                                <input 
                                    type="checkbox" 
                                    :checked="member.hasPlayed"
                                    :disabled="playerSelectionDialog.readonly"
                                    @change="togglePlayerPlayed(member)"
                                />
                            </td>
                        </tr>
                    </tbody>
                </table>
                
                <div v-if="playerSelectionDialog.members.length === 0" class="no-members">
                    {{ $t('schedule.noActiveMembers') }}
                </div>
            </div>
            
            <div class="dialog-actions">
                <button v-if="!playerSelectionDialog.readonly" @click="savePlayerSelection" class="btn-save">{{ $t('schedule.save') }}</button>
                <button @click="closePlayerSelectionDialog" class="btn-cancel">{{ $t('schedule.cancel') }}</button>
            </div>
        </div>
    </BaseDialog>

    <BaseDialog
        v-model="friendlyResultDialog.isOpen"
        :title="`Ergebniseingabe - ${friendlyResultDialog.match?.homeTeam?.name || ''} vs ${friendlyResultDialog.match?.guestTeam?.name || ''}`"
        width="96vw"
        max-width="1500px"
        @close="closeFriendlyResultDialog"
    >
        <div class="friendly-result-dialog" v-if="friendlyResultDialog.match">
            <div class="score-summary">
                <div class="score-display">
                    <span class="score-label">Spielstand:</span>
                    <span :class="['score-value', friendlyScorePerspectiveClass(friendlyResultScore)]">{{ friendlyResultScore.home }}:{{ friendlyResultScore.guest }}</span>
                </div>
            </div>
            <table class="friendly-result-table">
                <thead>
                    <tr>
                        <th>#</th>
                        <th>Typ</th>
                        <th>Heim</th>
                        <th>Gast</th>
                        <th>Satz 1</th>
                        <th>Satz 2</th>
                        <th>Satz 3</th>
                        <th>Satz 4</th>
                        <th>Satz 5</th>
                        <th>Sätze</th>
                        <th>Punkte</th>
                        <th>Status</th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="(row, index) in friendlyResultDialog.rows" :key="row.id">
                        <td>{{ index + 1 }}</td>
                        <td>{{ row.type === 'double' ? 'Doppel' : 'Einzel' }}</td>
                        <td class="friendly-result-player">{{ row.homeName || '-' }}</td>
                        <td class="friendly-result-player">{{ row.guestName || '-' }}</td>
                        <td v-for="setIndex in 5" :key="setIndex">
                            <input
                                v-model="row.sets[setIndex - 1]"
                                class="set-input"
                                placeholder="11:7"
                                :disabled="friendlyResultReadonly || isFriendlySetClosed(row, setIndex - 1)"
                                @blur="normalizeFriendlySet(row, setIndex - 1)"
                            />
                        </td>
                        <td :class="['friendly-result-score-cell', friendlyScorePerspectiveClass(calculateFriendlyRowSets(row))]">{{ friendlyRowSetScore(row) }}</td>
                        <td :class="['friendly-result-score-cell', friendlyScorePerspectiveClass(friendlyRowPointScoreObject(row))]">{{ friendlyRowPointScore(row) }}</td>
                        <td>
                            <button type="button" class="btn-secondary" :disabled="friendlyResultReadonly" @click="row.completed = !row.completed">
                                {{ row.completed ? 'Abgeschlossen' : 'Offen' }}
                            </button>
                        </td>
                    </tr>
                </tbody>
                <tfoot>
                    <tr class="friendly-result-total-row">
                        <td colspan="9">Gesamt</td>
                        <td :class="['friendly-result-score-cell', friendlyScorePerspectiveClass(friendlyResultSetScore)]">{{ friendlyResultSetScore.home }}:{{ friendlyResultSetScore.guest }}</td>
                        <td :class="['friendly-result-score-cell', friendlyScorePerspectiveClass(friendlyResultScore)]">{{ friendlyResultScore.home }}:{{ friendlyResultScore.guest }}</td>
                        <td></td>
                    </tr>
                </tfoot>
            </table>
            <div v-if="friendlyResultDialog.error" class="friendly-result-error">{{ friendlyResultDialog.error }}</div>
            <div class="dialog-actions">
                <button v-if="!friendlyResultReadonly" @click="saveFriendlyResults(false)" class="btn-save">Speichern</button>
                <button v-if="!friendlyResultReadonly" @click="completeFriendlyResults" class="btn-save">Abschließen</button>
                <button @click="closeFriendlyResultDialog" class="btn-cancel">{{ $t('schedule.cancel') }}</button>
            </div>
        </div>
    </BaseDialog>

    <BaseDialog
        v-model="locationDialog.isOpen"
        :title="$t('schedule.locationDialogTitle')"
        :max-width="520"
        @close="closeLocationDialog"
    >
        <div v-if="locationDialog.match" class="schedule-location-dialog">
            <div class="schedule-location-row">
                <span class="schedule-location-label">{{ $t('schedule.locationName') }}</span>
                <strong>{{ locationDialog.match.location?.name || 'N/A' }}</strong>
            </div>
            <div class="schedule-location-row">
                <span class="schedule-location-label">{{ $t('schedule.addressLabel') }}</span>
                <span>{{ locationDialog.match.location?.address || 'N/A' }}</span>
            </div>
            <div class="schedule-location-row">
                <span class="schedule-location-label">{{ $t('schedule.cityLabel') }}</span>
                <span>{{ [locationDialog.match.location?.zip, locationDialog.match.location?.city].filter(Boolean).join(' ') || 'N/A' }}</span>
            </div>
            <div class="schedule-location-row">
                <span class="schedule-location-label">{{ $t('schedule.matchLabel') }}</span>
                <span>{{ locationDialog.match.homeTeam?.name || 'N/A' }} - {{ locationDialog.match.guestTeam?.name || 'N/A' }}</span>
            </div>
            <div class="schedule-location-row">
                <span class="schedule-location-label">{{ $t('schedule.date') }}</span>
                <span>{{ formatDate(locationDialog.match.date) }}</span>
            </div>
            <div class="schedule-location-row">
                <span class="schedule-location-label">{{ $t('schedule.time') }}</span>
                <span>{{ locationDialog.match.time ? locationDialog.match.time.toString().slice(0, 5) + ' ' + $t('common.time') : 'N/A' }}</span>
            </div>
        </div>
    </BaseDialog>

    <BaseDialog
        v-model="friendlyInvitationDialog.isOpen"
        title="Verein einladen"
        :max-width="560"
        @close="closeFriendlyInvitationDialog"
    >
        <div class="friendly-invitation-form">
            <div class="friendly-form-grid">
                <label>Zielverein
                    <select v-model="friendlyInvitationDialog.form.toClubId">
                        <option value="">Bitte Verein wählen</option>
                        <option
                            v-for="club in friendlyInvitationTargetClubs"
                            :key="club.id"
                            :value="club.id"
                        >
                            {{ club.name }}
                        </option>
                    </select>
                </label>
                <label>Datum
                    <input v-model="friendlyInvitationDialog.form.date" type="date" />
                </label>
                <label>Startzeit
                    <input v-model="friendlyInvitationDialog.form.startTime" type="time" />
                </label>
                <label>Matchname
                    <input v-model="friendlyInvitationDialog.form.matchName" type="text" placeholder="z. B. Freundschaftsspiel Herren 1" />
                </label>
            </div>
            <label class="friendly-invitation-message">Nachricht (optional)
                <textarea v-model="friendlyInvitationDialog.form.message" rows="3" placeholder="Kurze Nachricht an den Zielverein"></textarea>
            </label>
            <div class="dialog-actions">
                <button type="button" class="btn-save" @click="saveFriendlyInvitation">Einladung senden</button>
                <button type="button" class="btn-cancel" @click="closeFriendlyInvitationDialog">{{ $t('schedule.cancel') }}</button>
            </div>
        </div>
    </BaseDialog>

    <BaseDialog
        v-model="friendlyMatchDialog.isOpen"
        :title="friendlyMatchDialog.editingId ? 'Freundschaftsspiel bearbeiten' : 'Freundschaftsspiel anlegen'"
        :max-width="900"
        @close="closeFriendlyMatchDialog"
    >
        <div class="friendly-form">
            <div class="friendly-form-grid">
                <label>Datum <input v-model="friendlyMatchDialog.form.date" type="date" :disabled="friendlyMatchDialog.readonly" /></label>
                <label>Uhrzeit <input v-model="friendlyMatchDialog.form.time" type="time" :disabled="friendlyMatchDialog.readonly" /></label>
                <label>Heimteam <input v-model="friendlyMatchDialog.form.homeTeamName" type="text" :disabled="friendlyMatchDialog.readonly" /></label>
                <label>Gastteam <input v-model="friendlyMatchDialog.form.guestTeamName" type="text" :disabled="friendlyMatchDialog.readonly" /></label>
                <label>Spielsystem
                    <select v-model="friendlyMatchDialog.form.matchSystem" :disabled="friendlyMatchDialog.readonly">
                        <option>Braunschweiger System</option>
                        <option>Bundessystem</option>
                        <option>Werner-Scheffler-System</option>
                        <option>Sechser-Paarkreuz-System</option>
                        <option>Europaliga-System</option>
                        <option>Swaythling-Cup-System</option>
                        <option>Corbillon-Cup-System</option>
                        <option>Modifiziertes Swaythling-Cup-System</option>
                        <option>Freies System</option>
                    </select>
                </label>
                <label>Gewinnsätze <input v-model.number="friendlyMatchDialog.form.winningSets" type="number" min="1" :disabled="friendlyMatchDialog.readonly" /></label>
                <label v-if="canSetFriendlyVenue" class="friendly-venue-field">Spiellokal
                    <select v-model="friendlyMatchDialog.selectedVenueId" :disabled="friendlyMatchDialog.readonly" @change="applyFriendlyVenue">
                        <option value="">Kein Spiellokal ausgewählt</option>
                        <option v-for="venue in friendlyMatchDialog.venues" :key="venue.id" :value="String(venue.id)">{{ venue.name }}</option>
                    </select>
                </label>
                <div v-else-if="friendlyVenueSummary" class="friendly-venue-summary">
                    <strong>Spiellokal</strong>
                    <span>{{ friendlyVenueSummary }}</span>
                </div>
            </div>

            <div class="friendly-participants">
                <FriendlyParticipantsColumn
                    title="Heim-Aufstellung"
                    :members="friendlyParticipantMemberOptions('homeParticipants')"
                    :participants="friendlyMatchDialog.form.homeParticipants"
                    :allow-members="!friendlyMatchDialog.readonly"
                    :allow-manual="false"
                    :readonly="friendlyMatchDialog.readonly"
                    @add-member="addFriendlyParticipant('homeParticipants', $event)"
                    @add-manual="addManualFriendlyParticipant('homeParticipants', $event)"
                    @remove="removeFriendlyParticipant('homeParticipants', $event)"
                />
                <FriendlyParticipantsColumn
                    title="Gast-Aufstellung"
                    :members="friendlyParticipantMemberOptions('guestParticipants')"
                    :participants="friendlyMatchDialog.form.guestParticipants"
                    :allow-members="friendlyParticipantMemberOptions('guestParticipants').length > 0"
                    :allow-manual="friendlyParticipantMemberOptions('guestParticipants').length === 0"
                    :readonly="friendlyMatchDialog.readonly"
                    :member-hint="friendlyGuestMemberHint"
                    @add-member="addFriendlyParticipant('guestParticipants', $event)"
                    @add-manual="addManualFriendlyParticipant('guestParticipants', $event)"
                    @remove="removeFriendlyParticipant('guestParticipants', $event)"
                />
            </div>

            <section v-if="friendlyEditDoubleRows().length" class="friendly-doubles-section">
                <h4>Doppel</h4>
                <div v-for="(row, index) in friendlyEditDoubleRows()" :key="row.id" class="friendly-double-row">
                    <strong>Doppel {{ index + 1 }}</strong>
                    <div class="friendly-double-side">
                        <span>Heim</span>
                        <select :value="friendlyDoublePart(row.homeName, 0)" :disabled="friendlyMatchDialog.readonly" @change="setFriendlyEditDoublePart(row, 'homeName', 0, $event.target.value)">
                            <option value="">Spieler 1</option>
                            <option v-for="name in friendlyEditDoubleOptions('homeParticipants', row, 'homeName', 0)" :key="`ed-h1-${row.id}-${name}`" :value="name">{{ name }}</option>
                        </select>
                        <select :value="friendlyDoublePart(row.homeName, 1)" :disabled="friendlyMatchDialog.readonly" @change="setFriendlyEditDoublePart(row, 'homeName', 1, $event.target.value)">
                            <option value="">Spieler 2</option>
                            <option v-for="name in friendlyEditDoubleOptions('homeParticipants', row, 'homeName', 1)" :key="`ed-h2-${row.id}-${name}`" :value="name">{{ name }}</option>
                        </select>
                    </div>
                    <div class="friendly-double-side">
                        <span>Gast</span>
                        <select :value="friendlyDoublePart(row.guestName, 0)" :disabled="friendlyMatchDialog.readonly" @change="setFriendlyEditDoublePart(row, 'guestName', 0, $event.target.value)">
                            <option value="">Spieler 1</option>
                            <option v-for="name in friendlyEditDoubleOptions('guestParticipants', row, 'guestName', 0)" :key="`ed-g1-${row.id}-${name}`" :value="name">{{ name }}</option>
                        </select>
                        <select :value="friendlyDoublePart(row.guestName, 1)" :disabled="friendlyMatchDialog.readonly" @change="setFriendlyEditDoublePart(row, 'guestName', 1, $event.target.value)">
                            <option value="">Spieler 2</option>
                            <option v-for="name in friendlyEditDoubleOptions('guestParticipants', row, 'guestName', 1)" :key="`ed-g2-${row.id}-${name}`" :value="name">{{ name }}</option>
                        </select>
                    </div>
                </div>
            </section>

            <div class="dialog-actions">
                <button v-if="!friendlyMatchDialog.readonly" @click="saveFriendlyMatch" class="btn-save">Speichern</button>
                <button v-if="friendlyMatchDialog.editingId && !friendlyMatchDialog.readonly" @click="deleteFriendlyMatch" class="btn-cancel">Löschen</button>
                <button @click="closeFriendlyMatchDialog" class="btn-cancel">{{ $t('schedule.cancel') }}</button>
            </div>
        </div>
    </BaseDialog>
</template>

<script>
import { mapGetters, mapActions } from 'vuex';
import apiClient from '../apiClient.js';
import PDFGenerator from '../components/PDFGenerator.js';
import { getSafeErrorMessage } from '../utils/errorMessages.js';
import SeasonSelector from '../components/SeasonSelector.vue';

import InfoDialog from '../components/InfoDialog.vue';
import ConfirmDialog from '../components/ConfirmDialog.vue';
import BaseDialog from '../components/BaseDialog.vue';
import CsvImportDialog from '../components/CsvImportDialog.vue';
import ScheduleLayoutShell from '../components/schedule/ScheduleLayoutShell.vue';
import FriendlyParticipantsColumn from '../components/schedule/FriendlyParticipantsColumn.vue';
import {
    connectSocket,
    disconnectSocket,
    onScheduleMatchUpdated,
    offScheduleMatchUpdated,
    onMatchReportSubmitted,
    offMatchReportSubmitted,
    onFriendlyInvitationCreated,
    offFriendlyInvitationCreated,
    onFriendlyInvitationAccepted,
    offFriendlyInvitationAccepted,
    onFriendlyInvitationDeclined,
    offFriendlyInvitationDeclined,
    onFriendlySharedMatchUpdated,
    offFriendlySharedMatchUpdated,
    onFriendlySharedMatchDeleted,
    offFriendlySharedMatchDeleted
} from '../services/socketService.js';
export default {
    name: 'ScheduleView',
    props: {
        friendlyOnly: {
            type: Boolean,
            default: false
        }
    },
    components: {
        SeasonSelector,
        InfoDialog,
        ConfirmDialog,
        BaseDialog,
        CsvImportDialog,
        ScheduleLayoutShell,
        FriendlyParticipantsColumn
    },
    computed: {
        ...mapGetters(['isAuthenticated', 'currentClub', 'clubs', 'currentClubName']),
        filteredScheduleTeams() {
            const query = this.teamSearchQuery.trim().toLowerCase();
            if (!query) {
                return this.teams;
            }
            return this.teams.filter((team) => {
                const teamName = team.name?.toLowerCase() || '';
                const leagueName = team.league?.name?.toLowerCase() || '';
                return teamName.includes(query) || leagueName.includes(query);
            });
        },
        completedMatchesCount() {
            return this.matches.filter(match => match.isCompleted).length;
        },
        pendingMatchesCount() {
            return this.matches.length - this.completedMatchesCount;
        },
        friendlyResultScore() {
            return this.calculateFriendlyResultScore(this.friendlyResultDialog.rows);
        },
        friendlyResultSetScore() {
            return this.calculateFriendlyResultSetScore(this.friendlyResultDialog.rows);
        },
        friendlyResultReadonly() {
            return this.isFriendlyMatchReadOnly(this.friendlyResultDialog.match);
        },
        friendlyGuestMemberHint() {
            const match = this.friendlyMatchDialog.match;
            if (!match?.isSharedFriendly) return '';
            if (this.isFriendlyMatchReadOnly(match)) return 'Der Termin ist verstrichen. Die Gastmannschaft ist nur sichtbar.';
            return 'Gastmitglieder sind erst nach Annahme sichtbar.';
        },
        canSetFriendlyVenue() {
            const match = this.friendlyMatchDialog.match;
            if (this.friendlyMatchDialog.readonly) return false;
            if (!match?.isSharedFriendly) return true;
            return Number(match.homeClubId) === Number(this.currentClub);
        },
        friendlyVenueSummary() {
            const form = this.friendlyMatchDialog.form;
            return [form.locationName, [form.locationAddress, [form.locationZip, form.locationCity].filter(Boolean).join(' ')].filter(Boolean).join(', ')]
                .filter(Boolean)
                .join(' - ');
        },
        nextScheduledMatchLabel() {
            const today = new Date();
            today.setHours(0, 0, 0, 0);
            const nextMatch = this.matches
                .filter(match => match.date)
                .map(match => ({ ...match, _date: new Date(match.date) }))
                .filter(match => !isNaN(match._date.getTime()) && match._date >= today)
                .sort((a, b) => a._date - b._date)[0];
            return nextMatch ? this.formatDate(nextMatch.date) : '';
        },
        workspaceDescription() {
            if (!this.selectedLeague) return '';
            return this.activeTab === 'table'
                ? this.$t('schedule.workspaceTableDescription', { count: this.leagueTable.length })
                : this.friendlyOnly
                    ? `Interne Freundschaftsspiele: ${this.matches.length}`
                : this.$t('schedule.workspaceScheduleDescription', {
                    matches: this.matches.length,
                    completed: this.completedMatchesCount,
                    pending: this.pendingMatchesCount
                });
        },
        leagueTeamOptions() {
            const ownTeamName = this.selectedTeam?.name;
            return Array.from(new Set(this.getCombinedLeagueMatches().flatMap(match => [
                match.homeTeam?.name,
                match.guestTeam?.name
            ].filter(Boolean))))
                .filter(teamName => teamName !== ownTeamName)
                .sort((a, b) => a.localeCompare(b));
        },
        friendlyMatchesLabel() {
            return 'Freundschaftsspiele';
        },
        friendlyInvitationTargetClubs() {
            return (this.clubs || []).filter((club) => Number(club.id) !== Number(this.currentClub));
        },
    },
    watch: {
        currentClub: {
            immediate: true,
            handler(newVal) {
                offScheduleMatchUpdated(this.handleScheduleMatchUpdated);
                offMatchReportSubmitted(this.handleMatchReportSubmitted);
                offFriendlyInvitationCreated(this.handleFriendlyInvitationRealtime);
                offFriendlyInvitationAccepted(this.handleFriendlyInvitationRealtime);
                offFriendlyInvitationDeclined(this.handleFriendlyInvitationRealtime);
                offFriendlySharedMatchUpdated(this.handleFriendlySharedMatchUpdatedRealtime);
                offFriendlySharedMatchDeleted(this.handleFriendlySharedMatchDeletedRealtime);
                disconnectSocket();
                if (newVal) {
                    connectSocket(newVal);
                    onScheduleMatchUpdated(this.handleScheduleMatchUpdated);
                    onMatchReportSubmitted(this.handleMatchReportSubmitted);
                    onFriendlyInvitationCreated(this.handleFriendlyInvitationRealtime);
                    onFriendlyInvitationAccepted(this.handleFriendlyInvitationRealtime);
                    onFriendlyInvitationDeclined(this.handleFriendlyInvitationRealtime);
                    onFriendlySharedMatchUpdated(this.handleFriendlySharedMatchUpdatedRealtime);
                    onFriendlySharedMatchDeleted(this.handleFriendlySharedMatchDeletedRealtime);
                    if (this.friendlyOnly) {
                        this.loadFriendlyMatches();
                        this.loadFriendlyInvitations();
                    }
                }
            }
        },
    },
    data() {
        return {
            // Dialog States
            infoDialog: {
                isOpen: false,
                title: '',
                message: '',
                details: '',
                type: 'info'
            },
            confirmDialog: {
                isOpen: false,
                title: '',
                message: '',
                details: '',
                type: 'info',
                resolveCallback: null
            },
            showImportModal: false,
            selectedFile: null,
            teams: [],
            matches: [],
            selectedLeague: '',
            selectedTeam: null,
            selectedSeasonId: null,
            currentSeason: null,
            activeTab: 'schedule',
            leagueTable: [],
            fetchingTable: false,
            fetchingTeamData: false,
            teamSearchQuery: '',
            ownLeagueMatches: [],
            allLeagueMatches: [],
            leagueMatchScope: 'own',
            selectedComparisonTeamName: '',
            friendlyMatches: [],
            
            // Player Selection Dialog
            playerSelectionDialog: {
                isOpen: false,
                match: null,
                members: [],
                loading: false,
                readonly: false
            },
            locationDialog: {
                isOpen: false,
                match: null
            },
            // Gallery Dialog
            showGalleryDialog: false,
            galleryLoading: false,
            galleryMembers: [],
            galleryError: '',
            gallerySize: 200,
            friendlyMatchDialog: {
                isOpen: false,
                editingId: null,
                match: null,
                members: [],
                homeMembers: [],
                guestMembers: [],
                venues: [],
                selectedVenueId: '',
                readonly: false,
                form: {
                    date: new Date().toISOString().slice(0, 10),
                    time: '',
                    homeTeamName: '',
                    guestTeamName: '',
                    locationName: '',
                    locationAddress: '',
                    locationZip: '',
                    locationCity: '',
                    matchSystem: 'Braunschweiger System',
                    singlesCount: 12,
                    doublesCount: 4,
                    winningSets: 3,
                    homeMatchPoints: 0,
                    guestMatchPoints: 0,
                    isCompleted: false,
                    homeParticipants: [],
                    guestParticipants: []
                }
            },
            friendlyResultDialog: {
                isOpen: false,
                match: null,
                rows: [],
                error: '',
                saving: false,
                saveAgain: false
            },
            incomingFriendlyInvitations: [],
            outgoingFriendlyInvitations: [],
            friendlyInvitationDialog: {
                isOpen: false,
                form: {
                    toClubId: '',
                    date: new Date().toISOString().slice(0, 10),
                    startTime: '',
                    matchName: `Freundschaftsspiel ${new Date().toISOString().slice(0, 10)}`,
                    message: '',
                }
            },
        };
    },
    methods: {
        filterRegularScheduleMatches(matches) {
            return (Array.isArray(matches) ? matches : []).filter((match) => !match?.isFriendly);
        },
        getClubNameById(clubId) {
            const club = (this.clubs || []).find((item) => Number(item.id) === Number(clubId));
            return club?.name || `Verein ${clubId}`;
        },
        resetFriendlyInvitationDialogForm() {
            this.friendlyInvitationDialog.form = {
                toClubId: '',
                date: new Date().toISOString().slice(0, 10),
                startTime: '',
                matchName: `Freundschaftsspiel ${new Date().toISOString().slice(0, 10)}`,
                message: '',
            };
        },
        async openFriendlyInvitationDialog() {
            if (!this.friendlyInvitationTargetClubs.length) {
                await this.showInfo('Hinweis', 'Keine weiteren Vereine vorhanden.', '', 'info');
                return;
            }
            this.resetFriendlyInvitationDialogForm();
            this.friendlyInvitationDialog.isOpen = true;
        },
        closeFriendlyInvitationDialog() {
            this.friendlyInvitationDialog.isOpen = false;
            this.resetFriendlyInvitationDialogForm();
        },
        async saveFriendlyInvitation() {
            const form = this.friendlyInvitationDialog.form;
            const toClubId = Number.parseInt(form.toClubId, 10);
            if (!Number.isInteger(toClubId)) {
                await this.showInfo('Hinweis', 'Bitte einen Zielverein auswählen.', '', 'info');
                return;
            }
            if (!String(form.date || '').trim()) {
                await this.showInfo('Hinweis', 'Bitte ein Datum angeben.', '', 'info');
                return;
            }
            if (!String(form.matchName || '').trim()) {
                await this.showInfo('Hinweis', 'Bitte einen Matchnamen angeben.', '', 'info');
                return;
            }
            try {
                await apiClient.post(`/friendly-match-invitations/${this.currentClub}`, {
                    toClubId,
                    date: form.date,
                    startTime: String(form.startTime || '').trim() || null,
                    matchName: String(form.matchName || '').trim(),
                    message: String(form.message || '').trim() || null,
                });
                this.closeFriendlyInvitationDialog();
                await this.loadFriendlyInvitations();
                await this.showInfo('Erfolg', 'Einladung wurde versendet.', '', 'success');
            } catch (error) {
                await this.showInfo('Fehler', getSafeErrorMessage(error, 'Einladung konnte nicht gespeichert werden.'), '', 'error');
            }
        },
        async loadFriendlyInvitations() {
            try {
                const [incomingResponse, outgoingResponse] = await Promise.all([
                    apiClient.get(`/friendly-match-invitations/${this.currentClub}/incoming`),
                    apiClient.get(`/friendly-match-invitations/${this.currentClub}/outgoing`),
                ]);
                const incoming = Array.isArray(incomingResponse.data) ? incomingResponse.data : [];
                const outgoing = Array.isArray(outgoingResponse.data) ? outgoingResponse.data : [];
                this.incomingFriendlyInvitations = incoming.filter((invitation) => String(invitation?.status || 'pending') === 'pending');
                this.outgoingFriendlyInvitations = outgoing.filter((invitation) => String(invitation?.status || 'pending') === 'pending');
            } catch (error) {
                console.error('Error loading friendly invitations:', error);
            }
        },
        async acceptFriendlyInvitation(invitationId) {
            try {
                await apiClient.post(`/friendly-match-invitations/${this.currentClub}/${invitationId}/accept`);
                this.incomingFriendlyInvitations = this.incomingFriendlyInvitations.filter((invitation) => invitation.id !== invitationId);
                this.outgoingFriendlyInvitations = this.outgoingFriendlyInvitations.filter((invitation) => invitation.id !== invitationId);
                await Promise.all([this.loadFriendlyInvitations(), this.loadFriendlyMatches()]);
            } catch (error) {
                await this.showInfo('Fehler', getSafeErrorMessage(error, 'Einladung konnte nicht angenommen werden.'), '', 'error');
            }
        },
        async declineFriendlyInvitation(invitationId) {
            try {
                await apiClient.post(`/friendly-match-invitations/${this.currentClub}/${invitationId}/decline`);
                this.incomingFriendlyInvitations = this.incomingFriendlyInvitations.filter((invitation) => invitation.id !== invitationId);
                this.outgoingFriendlyInvitations = this.outgoingFriendlyInvitations.filter((invitation) => invitation.id !== invitationId);
                await this.loadFriendlyInvitations();
            } catch (error) {
                await this.showInfo('Fehler', getSafeErrorMessage(error, 'Einladung konnte nicht abgelehnt werden.'), '', 'error');
            }
        },
        emptyFriendlyMatchForm() {
            const today = new Date().toISOString().slice(0, 10);
            return {
                date: today,
                time: '',
                homeTeamName: this.currentClubName || '',
                guestTeamName: '',
                locationName: '',
                locationAddress: '',
                locationZip: '',
                locationCity: '',
                matchSystem: 'Braunschweiger System',
                singlesCount: 12,
                doublesCount: 4,
                winningSets: 3,
                homeMatchPoints: 0,
                guestMatchPoints: 0,
                isCompleted: false,
                homeParticipants: [],
                guestParticipants: [],
                resultDetails: []
            };
        },
        parseFriendlyArray(value) {
            if (Array.isArray(value)) return value;
            if (typeof value === 'string') {
                try {
                    const parsed = JSON.parse(value);
                    return Array.isArray(parsed) ? parsed : [];
                } catch (error) {
                    return [];
                }
            }
            return [];
        },
        friendlyMatchEndsAt(match) {
            if (!match?.date) return null;
            const date = String(match.date).slice(0, 10);
            const value = new Date(`${date}T23:59:59`);
            return Number.isNaN(value.getTime()) ? null : value;
        },
        isFriendlyMatchReadOnly(match) {
            if (!match?.isFriendly) return false;
            if (match.isLocked === true) return true;
            const endsAt = this.friendlyMatchEndsAt(match);
            return endsAt ? endsAt.getTime() <= Date.now() : false;
        },
        sortFriendlyMembers(members) {
            return (members || []).slice().sort((a, b) => {
                const fa = (a.firstName || '').toString().toLowerCase();
                const fb = (b.firstName || '').toString().toLowerCase();
                if (fa < fb) return -1;
                if (fa > fb) return 1;
                return (a.lastName || '').toString().localeCompare((b.lastName || '').toString());
            });
        },
        friendlyMemberIdList(value) {
            if (Array.isArray(value)) return value.map((id) => Number(id)).filter((id) => Number.isFinite(id));
            if (typeof value === 'string') {
                try {
                    const parsed = JSON.parse(value);
                    return Array.isArray(parsed) ? parsed.map((id) => Number(id)).filter((id) => Number.isFinite(id)) : [];
                } catch (error) {
                    return [];
                }
            }
            return [];
        },
        friendlyEligibleMemberIds(match = this.friendlyMatchDialog.match) {
            const ready = new Set(this.friendlyMemberIdList(match?.playersReady));
            const planned = new Set(this.friendlyMemberIdList(match?.playersPlanned));
            if (!ready.size || !planned.size) return null;
            return new Set([...ready].filter((id) => planned.has(id)));
        },
        friendlyParticipantMemberOptions(field) {
            const source = field === 'guestParticipants' ? this.friendlyMatchDialog.guestMembers : this.friendlyMatchDialog.homeMembers;
            const eligible = this.friendlyEligibleMemberIds();
            if (!eligible) return source;
            const selected = new Set((this.friendlyMatchDialog.form[field] || [])
                .filter((participant) => participant?.type === 'member')
                .map((participant) => Number(participant.memberId)));
            return source.filter((member) => eligible.has(Number(member.id)) || selected.has(Number(member.id)));
        },
        sortMatchesByDateTime(matches) {
            if (!Array.isArray(matches)) {
                return [];
            }

            const toTimestamp = (match) => {
                if (!match?.date) {
                    return Number.POSITIVE_INFINITY;
                }

                const dateValue = new Date(match.date);
                if (isNaN(dateValue.getTime())) {
                    return Number.POSITIVE_INFINITY;
                }

                const timeString = typeof match.time === 'string' ? match.time : '';
                const [hours, minutes] = timeString.split(':').map((value) => parseInt(value, 10));

                if (!Number.isNaN(hours)) {
                    dateValue.setHours(hours, Number.isNaN(minutes) ? 0 : minutes, 0, 0);
                } else {
                    dateValue.setHours(0, 0, 0, 0);
                }

                return dateValue.getTime();
            };

            return [...matches].sort((a, b) => {
                const diff = toTimestamp(a) - toTimestamp(b);
                if (diff !== 0) {
                    return diff;
                }

                const homeA = a?.homeTeam?.name || '';
                const homeB = b?.homeTeam?.name || '';
                const guestA = a?.guestTeam?.name || '';
                const guestB = b?.guestTeam?.name || '';

                return `${homeA} ${guestA}`.localeCompare(`${homeB} ${guestB}`);
            });
        },
        getResultClass(match) {
            if (!match.isCompleted) {
                return '';
            }

            const home = Number(match.homeMatchPoints);
            const guest = Number(match.guestMatchPoints);

            // Beteiligung unseres Vereins prüfen
            const isOurTeamHome = this.isOurTeam(match.homeTeam?.name);
            const isOurTeamGuest = this.isOurTeam(match.guestTeam?.name);
            const isOurMatch = isOurTeamHome || isOurTeamGuest;

            // Unentschieden: Nur gelb markieren, wenn unser Team beteiligt ist
            if (!isNaN(home) && !isNaN(guest) && home === guest) {
                return isOurMatch ? 'completed draw' : 'completed';
            }

            if (isOurTeamHome) {
                return home > guest ? 'completed won' : 'completed lost';
            } else if (isOurTeamGuest) {
                return guest > home ? 'completed won' : 'completed lost';
            }

            return 'completed';
        },

        isOurTeam(teamName) {
            if (!teamName || !this.currentClubName) {
                return false;
            }
            // Check if team name starts with our club name
            return teamName.startsWith(this.currentClubName);
        },

        isOurClubPlayingHome(match) {
            const club = (this.currentClubName || '').toString().trim().toLowerCase();
            if (!club || !match) return false;
            const homeName = (match.homeTeam?.name || '').toString().toLowerCase();
            const guestName = (match.guestTeam?.name || '').toString().toLowerCase();
            const homeHas = homeName.includes(club);
            const guestHas = guestName.includes(club);
            if (homeHas && guestHas) return true; // both teams from our club -> treat as home
            if (homeHas) return true;
            if (guestHas) return false;
            // Fallback: if neither includes club name, prefer original startsWith heuristic
            return homeName.startsWith(this.currentClubName || '') || false;
        },

        // Dialog Helper Methods
        async showInfo(title, message, details = '', type = 'info') {
            this.infoDialog = {
                isOpen: true,
                title,
                message,
                details,
                type
            };
        },

        async showConfirm(title, message, details = '', type = 'info') {
            return new Promise((resolve) => {
                this.confirmDialog = {
                    isOpen: true,
                    title,
                    message,
                    details,
                    type,
                    resolveCallback: resolve
                };
            });
        },

        handleConfirmResult(confirmed) {
            if (this.confirmDialog.resolveCallback) {
                this.confirmDialog.resolveCallback(confirmed);
                this.confirmDialog.resolveCallback = null;
            }
            this.confirmDialog.isOpen = false;
        },

        async openPlayerSelectionDialog(match) {
            this.playerSelectionDialog.match = match;
            this.playerSelectionDialog.readonly = this.isFriendlyMatchReadOnly(match);
            this.playerSelectionDialog.isOpen = true;
            this.playerSelectionDialog.loading = true;
            
            try {
                const normalizePlayersList = (value) => {
                    if (Array.isArray(value)) return value;
                    if (typeof value === 'string') {
                        try {
                            const parsed = JSON.parse(value);
                            return Array.isArray(parsed) ? parsed : [];
                        } catch (e) {
                            return [];
                        }
                    }
                    return [];
                };
                const readyIds = normalizePlayersList(match.playersReady);
                const plannedIds = normalizePlayersList(match.playersPlanned);
                const playedIds = normalizePlayersList(match.playersPlayed);
                const preselectedIds = Array.from(new Set([...readyIds, ...plannedIds, ...playedIds]));

                let allMembers = [];
                if (match.isFriendly && match.isSharedFriendly) {
                    const ownSide = Number(match.homeClubId) === Number(this.currentClub) ? 'home' : 'guest';
                    const response = await apiClient.get(`/friendly-matches/shared/${this.currentClub}/${match.id}/members/${ownSide}`);
                    allMembers = response.data || [];
                } else {
                    const response = match.isFriendly
                        ? await apiClient.get(`/friendly-matches/${this.currentClub}/members/list`)
                        : await apiClient.get(`/clubmembers/get/${this.currentClub}/true`);
                    allMembers = response.data || [];
                }

                const lineupHalf = this.getLineupHalfForMatch(match);
                const eligibleMemberIds = match.isFriendly ? [] : await this.getEligibleMemberIdsForSelectedTeam(lineupHalf);
                const activeMembers = match.isFriendly ? allMembers : allMembers.filter(member => member.active);
                const allowedIds = new Set(
                    [...eligibleMemberIds]
                        .map((id) => Number(id))
                        .filter((id) => Number.isFinite(id))
                );
                const visibleMembers = allowedIds.size > 0
                    ? activeMembers.filter((member) => allowedIds.has(Number(member.id)))
                    : activeMembers;

                // Sort members alphabetically by firstName then lastName (case-insensitive)
                const sortedVisibleMembers = (visibleMembers || []).slice().sort((a, b) => {
                    const fa = (a.firstName || '').toString().toLowerCase();
                    const fb = (b.firstName || '').toString().toLowerCase();
                    if (fa < fb) return -1;
                    if (fa > fb) return 1;
                    const la = (a.lastName || '').toString().toLowerCase();
                    const lb = (b.lastName || '').toString().toLowerCase();
                    return la.localeCompare(lb);
                });

                this.playerSelectionDialog.members = sortedVisibleMembers.map(m => ({
                    ...m,
                    isReady: readyIds.includes(m.id) || false,
                    isPlanned: plannedIds.includes(m.id) || false,
                    hasPlayed: playedIds.includes(m.id) || false
                }));
                    
            } catch (error) {
                console.error('Error loading members:', error);
                console.error('Error details:', error.response?.data);
                await this.showInfo(this.$t('messages.error'), this.$t('schedule.errorLoadingMembers'), getSafeErrorMessage(error), 'error');
            } finally {
                this.playerSelectionDialog.loading = false;
            }
        },
        getLineupHalfForMatch(match) {
            const matchDate = new Date(match?.date);
            if (Number.isNaN(matchDate.getTime())) {
                return 'first_half';
            }
            // Rueckrunde: Januar bis Juni
            return matchDate.getMonth() <= 5 ? 'second_half' : 'first_half';
        },
        async getEligibleMemberIdsForSelectedTeam(lineupHalf) {
            if (!this.selectedTeam?.id) {
                return [];
            }

            const selectedIndex = this.teams.findIndex(team => Number(team.id) === Number(this.selectedTeam.id));
            if (selectedIndex < 0) {
                return [];
            }

            // Niedrigere Mannschaften = ausgewaehltes Team + alle dahinter in der sortierten Teamliste
            const candidateTeams = this.teams.slice(selectedIndex);
            const lineupResponses = await Promise.all(candidateTeams.map(async (team) => {
                try {
                    const response = await apiClient.get(`/club-teams/${team.id}/lineup`, {
                        params: { half: lineupHalf }
                    });
                    return Array.isArray(response.data) ? response.data : [];
                } catch (error) {
                    return [];
                }
            }));

            const ids = new Set();
            lineupResponses.flat().forEach((assignment) => {
                const memberId = Number(assignment?.memberId);
                if (Number.isFinite(memberId)) {
                    ids.add(memberId);
                }
            });
            return Array.from(ids);
        },
        
        closePlayerSelectionDialog() {
            this.playerSelectionDialog.isOpen = false;
            this.playerSelectionDialog.match = null;
            this.playerSelectionDialog.members = [];
        },
        openLocationDialog(match) {
            if (!match?.location) {
                return;
            }
            this.locationDialog.isOpen = true;
            this.locationDialog.match = match;
        },
        closeLocationDialog() {
            this.locationDialog.isOpen = false;
            this.locationDialog.match = null;
        },
        
        togglePlayerReady(member) {
            member.isReady = !member.isReady;
        },
        
        togglePlayerPlanned(member) {
            member.isPlanned = !member.isPlanned;
        },
        
        togglePlayerPlayed(member) {
            member.hasPlayed = !member.hasPlayed;
        },
        
        async savePlayerSelection(closeDialog = true) {
            const match = this.playerSelectionDialog.match;
            if (!match || this.isFriendlyMatchReadOnly(match)) return;
            
            const normalizePlayersList = (value) => {
                if (Array.isArray(value)) return value.map((id) => Number(id)).filter((id) => Number.isFinite(id));
                if (typeof value === 'string') {
                    try {
                        const parsed = JSON.parse(value);
                        return Array.isArray(parsed) ? parsed.map((id) => Number(id)).filter((id) => Number.isFinite(id)) : [];
                    } catch (error) {
                        return [];
                    }
                }
                return [];
            };
            const visibleIds = new Set(this.playerSelectionDialog.members.map((m) => Number(m.id)));
            const mergeVisibleSelection = (existing, predicate) => [
                ...normalizePlayersList(existing).filter((id) => !visibleIds.has(Number(id))),
                ...this.playerSelectionDialog.members.filter(predicate).map((m) => Number(m.id)),
            ].filter((id, index, arr) => Number.isFinite(id) && arr.indexOf(id) === index);
            const playersReady = mergeVisibleSelection(match.playersReady, (m) => m.isReady);
            const playersPlanned = mergeVisibleSelection(match.playersPlanned, (m) => m.isPlanned);
            const playersPlayed = mergeVisibleSelection(match.playersPlayed, (m) => m.hasPlayed);
            
            console.log('[savePlayerSelection] Saving players:', { playersReady, playersPlanned, playersPlayed, matchId: match.id });
            
            try {
                const response = match.isFriendly
                    ? await apiClient.patch(`${match.isSharedFriendly ? `/friendly-matches/shared/${this.currentClub}/${match.id}/players` : `/friendly-matches/${this.currentClub}/${match.id}/players`}`, {
                        playersReady,
                        playersPlanned,
                        playersPlayed
                    })
                    : await apiClient.patch(`/matches/${match.id}/players`, {
                        clubId: this.currentClub,
                        playersReady,
                        playersPlanned,
                        playersPlayed
                    });
                if (response.status >= 400) {
                    throw new Error(response?.data?.error || 'Failed to update match players');
                }
                console.log('[savePlayerSelection] API response:', response);
                
                // Update local match data
                match.playersReady = playersReady;
                match.playersPlanned = playersPlanned;
                match.playersPlayed = playersPlayed;
                
                // Update all members in the list to reflect the current state
                this.playerSelectionDialog.members.forEach(m => {
                    m.isReady = playersReady.includes(m.id);
                    m.isPlanned = playersPlanned.includes(m.id);
                    m.hasPlayed = playersPlayed.includes(m.id);
                });
                
                if (closeDialog) {
                    await this.showInfo(this.$t('messages.success'), this.$t('schedule.playerSelectionSaved'), '', 'success');
                    this.closePlayerSelectionDialog();
                }
            } catch (error) {
                console.error('Error saving player selection:', error);
                await this.showInfo(this.$t('messages.error'), this.$t('schedule.errorSavingPlayerSelection'), '', 'error');
            }
        },

        // Gallery Methods
        async openGalleryDialog() {
            if (!this.playerSelectionDialog.match) {
                await this.showInfo(this.$t('messages.info'), this.$t('schedule.pleaseSelectGame'), '', 'info');
                return;
            }
            this.showGalleryDialog = true;
            await this.loadGalleryMembers();
        },

        async loadGalleryMembers() {
            if (!this.currentClub || this.galleryLoading) {
                return;
            }
            this.galleryLoading = true;
            this.galleryError = '';
            try {
                const response = await apiClient.get(`/clubmembers/gallery/${this.currentClub}?format=json&size=${this.gallerySize}`);
                this.galleryMembers = response.data.members || [];
            } catch (error) {
                console.error('Fehler beim Laden der Galerie:', error);
                this.galleryError = error?.response?.data?.error || this.$t('schedule.errorLoadingGallery');
                this.galleryMembers = [];
            } finally {
                this.galleryLoading = false;
            }
        },

        closeGalleryDialog() {
            this.showGalleryDialog = false;
            this.galleryMembers = [];
        },

        getMemberImageUrl(memberId) {
            const token = this.$store.getters.token;
            const backendBaseUrl = import.meta.env.VITE_BACKEND || 'http://localhost:3005';
            // Get primary image (latest will be handled by backend)
            return `${backendBaseUrl}/api/clubmembers/image/${this.currentClub}/${memberId}?authcode=${token}`;
        },

        isMemberReady(memberId) {
            const match = this.playerSelectionDialog.match;
            if (!match || !match.playersReady) {
                return false;
            }
            return match.playersReady.includes(memberId);
        },

        async toggleMemberReady(member) {
            console.log('[toggleMemberReady] Called with member:', member);
            const match = this.playerSelectionDialog.match;
            if (!match) {
                console.error('[toggleMemberReady] No match selected');
                return;
            }
            console.log('[toggleMemberReady] Match:', match.id);

            // Find member in playerSelectionDialog.members
            let memberInList = this.playerSelectionDialog.members.find(m => m.id === member.memberId);
            if (!memberInList) {
                // Member not in list yet, add it
                try {
                    const response = await apiClient.get(`/clubmembers/get/${this.currentClub}/true`);
                    const allMembers = response.data;
                    const foundMember = allMembers.find(m => m.id === member.memberId);
                    if (foundMember) {
                        // Check if member is already marked as ready
                        const isCurrentlyReady = match.playersReady?.includes(member.memberId) || false;
                        memberInList = {
                            ...foundMember,
                            isReady: isCurrentlyReady,
                            isPlanned: match.playersPlanned?.includes(member.memberId) || false,
                            hasPlayed: match.playersPlayed?.includes(member.memberId) || false
                        };
                        this.playerSelectionDialog.members.push(memberInList);
                    } else {
                        console.error('Member not found:', member.memberId);
                        return;
                    }
                } catch (error) {
                    console.error('Error loading member:', error);
                    return;
                }
            }

            // Toggle ready status
            const wasReady = memberInList.isReady;
            memberInList.isReady = !wasReady;

            // Update match.playersReady immediately
            if (!match.playersReady) {
                match.playersReady = [];
            }
            const index = match.playersReady.indexOf(member.memberId);
            if (wasReady && index > -1) {
                match.playersReady.splice(index, 1);
            } else if (!wasReady && index === -1) {
                match.playersReady.push(member.memberId);
            }

            // Auto-save (don't close dialog if it's open)
            console.log('[toggleMemberReady] Calling savePlayerSelection');
            await this.savePlayerSelection(false);
            console.log('[toggleMemberReady] savePlayerSelection completed');
        },

        ...mapActions(['openDialog']),
        openImportModal() {
            this.showImportModal = true;
        },
        closeImportModal() {
            this.showImportModal = false;
            this.selectedFile = null;
        },
        handleCsvImport(file) {
            this.selectedFile = file;
            this.importCSV();
        },
        async loadFriendlyVenues(match = null) {
            this.friendlyMatchDialog.venues = [];
            this.friendlyMatchDialog.selectedVenueId = '';
            if (!this.currentClub) return;
            const canLoad = !match?.isSharedFriendly || Number(match.homeClubId) === Number(this.currentClub);
            if (!canLoad) return;
            try {
                const response = await apiClient.get(`/club-venues/${this.currentClub}`);
                this.friendlyMatchDialog.venues = response.data || [];
            } catch (error) {
                this.friendlyMatchDialog.venues = [];
            }
        },
        findFriendlyVenueForForm() {
            const form = this.friendlyMatchDialog.form;
            return this.friendlyMatchDialog.venues.find((venue) => (
                String(venue.name || '') === String(form.locationName || '')
                && String(venue.address || '') === String(form.locationAddress || '')
                && String(venue.zip || '') === String(form.locationZip || '')
                && String(venue.city || '') === String(form.locationCity || '')
            ));
        },
        applyFriendlyVenue() {
            const venue = this.friendlyMatchDialog.venues.find((item) => String(item.id) === String(this.friendlyMatchDialog.selectedVenueId));
            if (!venue) {
                this.friendlyMatchDialog.form.locationName = '';
                this.friendlyMatchDialog.form.locationAddress = '';
                this.friendlyMatchDialog.form.locationZip = '';
                this.friendlyMatchDialog.form.locationCity = '';
                return;
            }
            this.friendlyMatchDialog.form.locationName = venue.name || '';
            this.friendlyMatchDialog.form.locationAddress = venue.address || '';
            this.friendlyMatchDialog.form.locationZip = venue.zip || '';
            this.friendlyMatchDialog.form.locationCity = venue.city || '';
        },
        async loadFriendlyMembers(match = null) {
            const localMembers = async () => {
                const response = await apiClient.get(`/friendly-matches/${this.currentClub}/members/list`);
                return this.sortFriendlyMembers(response.data || []);
            };
            if (match?.isSharedFriendly) {
                const [homeResponse, guestResponse] = await Promise.all([
                    apiClient.get(`/friendly-matches/shared/${this.currentClub}/${match.id}/members/home`),
                    apiClient.get(`/friendly-matches/shared/${this.currentClub}/${match.id}/members/guest`),
                ]);
                this.friendlyMatchDialog.homeMembers = this.sortFriendlyMembers(homeResponse.data || []);
                this.friendlyMatchDialog.guestMembers = this.sortFriendlyMembers(guestResponse.data || []);
                this.friendlyMatchDialog.members = [...this.friendlyMatchDialog.homeMembers, ...this.friendlyMatchDialog.guestMembers];
                return;
            }
            const members = await localMembers();
            this.friendlyMatchDialog.members = members;
            this.friendlyMatchDialog.homeMembers = members;
            this.friendlyMatchDialog.guestMembers = [];
        },
        async openFriendlyMatchDialog(match = null) {
            await this.loadFriendlyMembers(match);
            this.friendlyMatchDialog.match = match?.isFriendly ? match : null;
            this.friendlyMatchDialog.editingId = match?.isFriendly ? match.id : null;
            this.friendlyMatchDialog.readonly = this.isFriendlyMatchReadOnly(match);
            this.friendlyMatchDialog.form = match?.isFriendly
                ? {
                    date: match.date ? String(match.date).slice(0, 10) : new Date().toISOString().slice(0, 10),
                    time: match.time ? String(match.time).slice(0, 5) : '',
                    homeTeamName: match.homeTeam?.name || '',
                    guestTeamName: match.guestTeam?.name || '',
                    locationName: match.location?.name === 'N/A' ? '' : (match.location?.name || ''),
                    locationAddress: match.location?.address || '',
                    locationZip: match.location?.zip || '',
                    locationCity: match.location?.city || '',
                    matchSystem: match.matchSystem || 'Braunschweiger System',
                    singlesCount: match.singlesCount ?? 12,
                    doublesCount: match.doublesCount ?? 4,
                    winningSets: match.winningSets ?? 3,
                    homeMatchPoints: match.homeMatchPoints ?? 0,
                    guestMatchPoints: match.guestMatchPoints ?? 0,
                    isCompleted: Boolean(match.isCompleted),
                    homeParticipants: [...this.parseFriendlyArray(match.homeParticipants)],
                    guestParticipants: [...this.parseFriendlyArray(match.guestParticipants)],
                    resultDetails: [...this.parseFriendlyArray(match.resultDetails)]
                }
                : this.emptyFriendlyMatchForm();
            await this.loadFriendlyVenues(match);
            const selectedVenue = this.findFriendlyVenueForForm();
            this.friendlyMatchDialog.selectedVenueId = selectedVenue ? String(selectedVenue.id) : '';
            this.friendlyMatchDialog.isOpen = true;
        },
        closeFriendlyMatchDialog() {
            this.friendlyMatchDialog.isOpen = false;
            this.friendlyMatchDialog.editingId = null;
            this.friendlyMatchDialog.match = null;
            this.friendlyMatchDialog.readonly = false;
            this.friendlyMatchDialog.venues = [];
            this.friendlyMatchDialog.selectedVenueId = '';
            this.friendlyMatchDialog.form = this.emptyFriendlyMatchForm();
        },
        addFriendlyParticipant(field, memberId) {
            const list = this.friendlyMatchDialog.form[field];
            if (list.some(p => p.type === 'member' && Number(p.memberId) === Number(memberId))) return;
            list.push({ type: 'member', memberId });
        },
        addManualFriendlyParticipant(field, fullName) {
            const parts = String(fullName).trim().split(/\s+/);
            const firstName = parts.shift() || '';
            const lastName = parts.join(' ');
            this.friendlyMatchDialog.form[field].push({ type: 'manual', firstName, lastName });
        },
        removeFriendlyParticipant(field, index) {
            this.friendlyMatchDialog.form[field].splice(index, 1);
        },
        friendlyParticipantLabel(participant, fallback = '') {
            if (!participant) return fallback;
            if (participant.type === 'member') {
                const member = [...this.friendlyMatchDialog.homeMembers, ...this.friendlyMatchDialog.guestMembers, ...this.friendlyMatchDialog.members].find(m => Number(m.id) === Number(participant.memberId));
                return member ? `${member.firstName} ${member.lastName}`.trim() : fallback;
            }
            return `${participant.firstName || ''} ${participant.lastName || ''}`.trim() || fallback;
        },
        friendlyParticipantLabels(participants) {
            return (Array.isArray(participants) ? participants : [])
                .map((participant) => this.friendlyParticipantLabel(participant, ''))
                .filter(Boolean);
        },
        friendlyDoubleLabel(labels, index) {
            if (!labels.length) return '';
            if (labels.length === 1) return labels[0];
            const pairings = [
                [0, 1],
                [2, 3],
                [0, 2],
                [1, 3]
            ];
            const fallbackStart = (index * 2) % labels.length;
            const [firstIndex, secondIndex] = pairings[index] || [fallbackStart, fallbackStart + 1];
            const first = labels[firstIndex % labels.length];
            const second = labels[secondIndex % labels.length];
            return first === second ? first : `${first} / ${second}`;
        },
        friendlyDoublePart(value, index) {
            return String(value || '').split('/').map((part) => part.trim())[index] || '';
        },
        setFriendlyDoublePart(row, field, index, value) {
            const parts = [this.friendlyDoublePart(row[field], 0), this.friendlyDoublePart(row[field], 1)];
            parts[index] = value;
            row[field] = parts.filter(Boolean).join(' / ');
            this.autoSaveFriendlyResults();
        },
        friendlyResultSideLabels(side) {
            const participants = side === 'guest'
                ? this.friendlyResultDialog.match?.guestParticipants
                : this.friendlyResultDialog.match?.homeParticipants;
            return this.friendlyParticipantLabels(participants);
        },
        friendlyResultNameOptions(side, row, field, doubleIndex = null) {
            const labels = this.friendlyResultSideLabels(side);
            const used = new Set();
            for (const candidate of this.friendlyResultDialog.rows || []) {
                if (candidate === row) continue;
                if (candidate.type === 'double') {
                    this.friendlyDoublePart(candidate[field], 0) && used.add(this.friendlyDoublePart(candidate[field], 0));
                    this.friendlyDoublePart(candidate[field], 1) && used.add(this.friendlyDoublePart(candidate[field], 1));
                } else if (candidate[field]) {
                    used.add(candidate[field]);
                }
            }
            if (doubleIndex != null) {
                const other = this.friendlyDoublePart(row[field], doubleIndex === 0 ? 1 : 0);
                if (other) used.add(other);
            }
            const current = doubleIndex == null ? row[field] : this.friendlyDoublePart(row[field], doubleIndex);
            return labels.filter((label) => label === current || !used.has(label));
        },
        friendlyEditDoubleRows() {
            const form = this.friendlyMatchDialog.form;
            const template = this.friendlyResultTemplate({
                ...form,
                homeParticipants: form.homeParticipants,
                guestParticipants: form.guestParticipants,
            });
            const doubleIds = template.filter((row) => row.type === 'double').map((row) => row.id);
            if (!Array.isArray(form.resultDetails)) form.resultDetails = [];
            const existingById = new Map(form.resultDetails
                .filter((row) => row?.id)
                .map((row) => [String(row.id), row]));
            const singles = form.resultDetails.filter((row) => row?.type !== 'double');
            const doubles = doubleIds.map((id) => {
                const row = existingById.get(id) || { id, type: 'double', homeName: '', guestName: '', sets: ['', '', '', '', ''], completed: false };
                row.id = id;
                row.type = 'double';
                row.sets = Array.from({ length: 5 }, (_, i) => row.sets?.[i] || '');
                return row;
            });
            form.resultDetails = [...doubles, ...singles];
            return doubles;
        },
        friendlyEditDoubleLabels(field) {
            return this.friendlyParticipantLabels(this.friendlyMatchDialog.form[field]);
        },
        setFriendlyEditDoublePart(row, field, index, value) {
            const parts = [this.friendlyDoublePart(row[field], 0), this.friendlyDoublePart(row[field], 1)];
            parts[index] = value;
            row[field] = parts.filter(Boolean).join(' / ');
        },
        friendlyEditDoubleOptions(participantField, row, nameField, doubleIndex) {
            const labels = this.friendlyEditDoubleLabels(participantField);
            const rows = this.friendlyEditDoubleRows();
            const used = new Set();
            for (const candidate of rows) {
                if (candidate === row) continue;
                this.friendlyDoublePart(candidate[nameField], 0) && used.add(this.friendlyDoublePart(candidate[nameField], 0));
                this.friendlyDoublePart(candidate[nameField], 1) && used.add(this.friendlyDoublePart(candidate[nameField], 1));
            }
            const other = this.friendlyDoublePart(row[nameField], doubleIndex === 0 ? 1 : 0);
            if (other) used.add(other);
            const current = this.friendlyDoublePart(row[nameField], doubleIndex);
            return labels.filter((label) => label === current || !used.has(label));
        },
        friendlySystemKey(system) {
            return String(system || '').trim().toLowerCase();
        },
        friendlyResultTemplate(match) {
            const system = this.friendlySystemKey(match.matchSystem);
            const homeCount = this.friendlyParticipantLabels(match.homeParticipants).length;
            const guestCount = this.friendlyParticipantLabels(match.guestParticipants).length;
            const rows = (entries) => entries.map((entry, index) => ({
                id: entry.id || `${entry.type === 'double' ? 'd' : 's'}-${index + 1}`,
                type: entry.type,
                home: entry.home,
                guest: entry.guest,
            }));
            const d = (id, home, guest) => ({ id, type: 'double', home, guest });
            const s = (id, home, guest) => ({ id, type: 'single', home, guest });
            if (system.includes('bundessystem')) {
                return rows([
                    d('d-1', 'D1', 'D1'), d('d-2', 'D2', 'D2'),
                    s('s-1', 'A1', 'B1'), s('s-2', 'A2', 'B2'), s('s-3', 'A3', 'B3'), s('s-4', 'A4', 'B4'),
                    s('s-5', 'A1', 'B2'), s('s-6', 'A2', 'B1'), s('s-7', 'A3', 'B4'), s('s-8', 'A4', 'B3'),
                ]);
            }
            if (system.includes('werner')) {
                return rows([
                    d('d-1', 'D1', 'D1'), d('d-2', 'D2', 'D2'),
                    s('s-1', 'A1', 'B2'), s('s-2', 'A2', 'B1'), s('s-3', 'A3', 'B4'), s('s-4', 'A4', 'B3'),
                    s('s-5', 'A1', 'B1'), s('s-6', 'A2', 'B2'), s('s-7', 'A3', 'B3'), s('s-8', 'A4', 'B4'),
                ]);
            }
            if (system.includes('sechser')) {
                return rows([
                    d('d-1', 'D1', 'D1'), d('d-2', 'D2', 'D2'), d('d-3', 'D3', 'D3'),
                    s('s-1', 'A1', 'B2'), s('s-2', 'A2', 'B1'), s('s-3', 'A1', 'B1'), s('s-4', 'A2', 'B2'),
                    s('s-5', 'A3', 'B4'), s('s-6', 'A4', 'B3'), s('s-7', 'A3', 'B3'), s('s-8', 'A4', 'B4'),
                    s('s-9', 'A5', 'B6'), s('s-10', 'A6', 'B5'), s('s-11', 'A5', 'B5'), s('s-12', 'A6', 'B6'),
                    d('d-4', 'D1', 'D1'),
                ]);
            }
            if (system.includes('europaliga')) {
                return rows([
                    d('d-1', 'D1', 'D1'), d('d-2', 'D2', 'D2'), d('d-3', 'D3', 'D3'),
                    s('s-1', 'A1', 'B1'), s('s-2', 'A2', 'B2'), s('s-3', 'A1', 'B2'), s('s-4', 'A2', 'B1'),
                    s('s-5', 'A3', 'B3'), s('s-6', 'A4', 'B4'), s('s-7', 'A3', 'B4'), s('s-8', 'A4', 'B3'),
                    s('s-9', 'A5', 'B5'), s('s-10', 'A6', 'B6'), s('s-11', 'A5', 'B6'), s('s-12', 'A6', 'B5'),
                ]);
            }
            if (system.includes('corbillon')) {
                return rows([
                    s('s-1', 'A1', 'B1'), s('s-2', 'A2', 'B2'), d('d-1', 'D1', 'D1'), s('s-3', 'A1', 'B2'), s('s-4', 'A2', 'B1'),
                ]);
            }
            if (system.includes('modifiziertes swaythling')) {
                return rows([
                    s('s-1', 'A1', 'B2'), s('s-2', 'A2', 'B1'), s('s-3', 'A3', 'B3'), d('d-1', 'D1', 'D1'), s('s-4', 'A1', 'B1'), s('s-5', 'A3', 'B2'), s('s-6', 'A2', 'B3'),
                ]);
            }
            if (system.includes('swaythling')) {
                return rows([
                    s('s-1', 'A1', 'B1'), s('s-2', 'A2', 'B2'), s('s-3', 'A3', 'B3'), s('s-4', 'A1', 'B2'), s('s-5', 'A2', 'B1'),
                ]);
            }
            if (system.includes('braunschweiger')) {
                if (homeCount >= 4 && guestCount >= 4) {
                    return rows([
                        d('d-1', 'D1', 'D1'), d('d-2', 'D2', 'D2'),
                        s('s-1', 'A1', 'B1'), s('s-2', 'A2', 'B2'), s('s-3', 'A3', 'B3'), s('s-4', 'A4', 'B4'),
                        s('s-5', 'A1', 'B2'), s('s-6', 'A2', 'B1'), s('s-7', 'A3', 'B4'), s('s-8', 'A4', 'B3'),
                    ]);
                }
                if (homeCount >= 4 && guestCount <= 3) {
                    return rows([
                        d('d-1', 'D1', 'D1'),
                        s('s-1', 'A3', 'B3'), s('s-2', 'A1', 'B2'), s('s-3', 'A2', 'B1'), s('s-4', 'A4', 'B2'),
                        s('s-5', 'A1', 'B1'), s('s-6', 'A4', 'B3'), s('s-7', 'A2', 'B2'), s('s-8', 'A1', 'B3'), s('s-9', 'A3', 'B1'),
                    ]);
                }
                if (homeCount <= 3 && guestCount >= 4) {
                    return rows([
                        d('d-1', 'D1', 'D1'),
                        s('s-1', 'A3', 'B3'), s('s-2', 'A2', 'B1'), s('s-3', 'A1', 'B2'), s('s-4', 'A2', 'B4'),
                        s('s-5', 'A1', 'B1'), s('s-6', 'A3', 'B4'), s('s-7', 'A2', 'B2'), s('s-8', 'A3', 'B1'), s('s-9', 'A1', 'B3'),
                    ]);
                }
                return rows([
                    d('d-1', 'D1', 'D1'),
                    s('s-1', 'A1', 'B2'), s('s-2', 'A2', 'B1'), s('s-3', 'A3', 'B2'), s('s-4', 'A2', 'B3'),
                    s('s-5', 'A1', 'B1'), s('s-6', 'A3', 'B3'), s('s-7', 'A2', 'B2'), s('s-8', 'A3', 'B1'), s('s-9', 'A1', 'B3'),
                ]);
            }
            const doublesCount = Number.parseInt(match.doublesCount, 10) || 0;
            const singlesCount = Number.parseInt(match.singlesCount, 10) || 0;
            return rows([
                ...Array.from({ length: doublesCount }, (_, i) => d(`d-${i + 1}`, `D${i + 1}`, `D${i + 1}`)),
                ...Array.from({ length: singlesCount }, (_, i) => s(`s-${i + 1}`, `A${(i % Math.max(homeCount, 1)) + 1}`, `B${(i % Math.max(guestCount, 1)) + 1}`)),
            ]);
        },
        friendlyPlayerForCode(labels, code) {
            const match = String(code || '').match(/[AB](\d+)/i);
            if (!match) return '';
            return labels[Number(match[1]) - 1] || '';
        },
        friendlyDoubleForCode(match, labels, side, code) {
            const number = Number(String(code || '').match(/D[A-Z]?(\d+)/i)?.[1] || 1);
            const row = this.parseFriendlyArray(match.resultDetails).find((candidate) => String(candidate?.id) === `d-${number}`);
            const value = row?.[side === 'guest' ? 'guestName' : 'homeName'];
            return value || this.friendlyDoubleLabel(labels, number - 1);
        },
        buildGeneratedFriendlyResultRows(match) {
            const homeLabels = this.friendlyParticipantLabels(match.homeParticipants);
            const guestLabels = this.friendlyParticipantLabels(match.guestParticipants);
            return this.friendlyResultTemplate(match).map((templateRow) => ({
                id: templateRow.id,
                type: templateRow.type,
                homeName: templateRow.type === 'double'
                    ? this.friendlyDoubleForCode(match, homeLabels, 'home', templateRow.home)
                    : this.friendlyPlayerForCode(homeLabels, templateRow.home),
                guestName: templateRow.type === 'double'
                    ? this.friendlyDoubleForCode(match, guestLabels, 'guest', templateRow.guest)
                    : this.friendlyPlayerForCode(guestLabels, templateRow.guest),
                sets: ['', '', '', '', ''],
                completed: false
            }));
        },
        buildFriendlyResultRows(match) {
            const existing = this.parseFriendlyArray(match.resultDetails);
            const generated = this.buildGeneratedFriendlyResultRows(match);
            if (!existing.length) return generated;

            const existingById = new Map(existing
                .filter((row) => row?.id)
                .map((row) => [String(row.id), row]));

            return generated.map((generatedRow, index) => {
                const existingRow = existingById.get(String(generatedRow.id)) || existing[index] || null;
                if (!existingRow) return generatedRow;
                return {
                    ...generatedRow,
                    homeName: generatedRow.homeName || '',
                    guestName: generatedRow.guestName || '',
                    sets: Array.from({ length: 5 }, (_, i) => existingRow.sets?.[i] || generatedRow.sets?.[i] || ''),
                    completed: Boolean(existingRow.completed),
                };
            });
        },
        async openFriendlyResultDialog(match) {
            await this.loadFriendlyMembers(match);
            this.friendlyResultDialog.match = match;
            this.friendlyResultDialog.rows = this.buildFriendlyResultRows(match);
            this.friendlyResultDialog.error = '';
            this.friendlyResultDialog.isOpen = true;
        },
        closeFriendlyResultDialog() {
            this.friendlyResultDialog.isOpen = false;
            this.friendlyResultDialog.match = null;
            this.friendlyResultDialog.rows = [];
            this.friendlyResultDialog.error = '';
            this.friendlyResultDialog.saving = false;
            this.friendlyResultDialog.saveAgain = false;
        },
        async normalizeFriendlySet(row, index) {
            const value = String(row.sets[index] || '').trim();
            if (!value) {
                this.applyFriendlyRowCompletion(row);
                await this.autoSaveFriendlyResults();
                return;
            }
            const normalized = this.normalizeFriendlySetValue(value);
            if (!normalized) {
                this.friendlyResultDialog.error = 'Bitte gültige Sätze eingeben, z.B. 11:7, 7 oder -7.';
                return;
            }
            row.sets[index] = normalized;
            this.applyFriendlyRowCompletion(row);
            this.friendlyResultDialog.error = '';
            await this.autoSaveFriendlyResults();
        },
        normalizeFriendlySetValue(value) {
            const raw = String(value || '').trim();
            if (!raw) return '';
            if (raw.includes(':')) {
                const parts = raw.split(':');
                if (parts.length !== 2) return null;
                const a = Number(parts[0]);
                const b = Number(parts[1]);
                if (!Number.isInteger(a) || !Number.isInteger(b) || a < 0 || b < 0) return null;
                if (Math.max(a, b) < 11 || Math.abs(a - b) < 2) return null;
                return `${a}:${b}`;
            }
            const losing = Math.abs(Number(raw));
            if (!Number.isInteger(losing)) return null;
            const winning = losing < 10 ? 11 : losing + 2;
            return raw.startsWith('-') ? `${losing}:${winning}` : `${winning}:${losing}`;
        },
        getFriendlyWinningSets() {
            const value = Number.parseInt(this.friendlyResultDialog.match?.winningSets, 10);
            return Number.isInteger(value) && value > 0 ? value : 3;
        },
        calculateFriendlyRowState(row) {
            const requiredSets = this.getFriendlyWinningSets();
            let homeSets = 0;
            let guestSets = 0;
            for (let index = 0; index < (row.sets || []).length; index += 1) {
                const set = row.sets[index];
                const normalized = this.normalizeFriendlySetValue(set);
                if (!normalized) continue;
                const [home, guest] = normalized.split(':').map(Number);
                if (home > guest) homeSets += 1;
                if (guest > home) guestSets += 1;
                if (homeSets >= requiredSets || guestSets >= requiredSets) {
                    return {
                        winner: homeSets > guestSets ? 'home' : 'guest',
                        decisiveIndex: index
                    };
                }
            }
            return { winner: null, decisiveIndex: null };
        },
        calculateFriendlyRowSets(row) {
            const requiredSets = this.getFriendlyWinningSets();
            let homeSets = 0;
            let guestSets = 0;
            for (const set of row.sets || []) {
                const normalized = this.normalizeFriendlySetValue(set);
                if (!normalized) continue;
                const [home, guest] = normalized.split(':').map(Number);
                if (home > guest) homeSets += 1;
                if (guest > home) guestSets += 1;
                if (homeSets >= requiredSets || guestSets >= requiredSets) break;
            }
            return { home: homeSets, guest: guestSets };
        },
        friendlyRowSetScore(row) {
            const sets = this.calculateFriendlyRowSets(row);
            return `${sets.home}:${sets.guest}`;
        },
        friendlyRowPointScore(row) {
            const score = this.friendlyRowPointScoreObject(row);
            return `${score.home}:${score.guest}`;
        },
        friendlyRowPointScoreObject(row) {
            const winner = this.calculateFriendlyRowWinner(row);
            if (winner === 'home') return { home: 1, guest: 0 };
            if (winner === 'guest') return { home: 0, guest: 1 };
            return { home: 0, guest: 0 };
        },
        friendlyResultViewerSide() {
            const match = this.friendlyResultDialog.match;
            if (match?.isSharedFriendly) {
                if (Number(match.homeClubId) === Number(this.currentClub)) return 'home';
                if (Number(match.guestClubId) === Number(this.currentClub)) return 'guest';
            }
            return this.isOurClubPlayingHome(match) ? 'home' : 'guest';
        },
        friendlyScorePerspectiveClass(score) {
            const home = Number(score?.home || 0);
            const guest = Number(score?.guest || 0);
            if (home === guest) return 'score-even';
            const viewerSide = this.friendlyResultViewerSide();
            const viewerLeading = viewerSide === 'guest' ? guest > home : home > guest;
            return viewerLeading ? 'score-leading' : 'score-trailing';
        },
        calculateFriendlyRowWinner(row) {
            return this.calculateFriendlyRowState(row).winner;
        },
        applyFriendlyRowCompletion(row) {
            const state = this.calculateFriendlyRowState(row);
            row.completed = Boolean(state.winner);
            if (state.winner && Number.isInteger(state.decisiveIndex)) {
                row.sets = row.sets.map((set, index) => index > state.decisiveIndex ? '' : set);
            }
        },
        isFriendlySetClosed(row, index) {
            const state = this.calculateFriendlyRowState(row);
            return Boolean(state.winner && Number.isInteger(state.decisiveIndex) && index > state.decisiveIndex);
        },
        calculateFriendlyResultScore(rows) {
            return (rows || []).reduce((score, row) => {
                const winner = this.calculateFriendlyRowWinner(row);
                if (winner === 'home') score.home += 1;
                if (winner === 'guest') score.guest += 1;
                return score;
            }, { home: 0, guest: 0 });
        },
        calculateFriendlyResultSetScore(rows) {
            return (rows || []).reduce((score, row) => {
                const sets = this.calculateFriendlyRowSets(row);
                score.home += sets.home;
                score.guest += sets.guest;
                return score;
            }, { home: 0, guest: 0 });
        },
        isFriendlyResultComplete(rows = this.friendlyResultDialog.rows) {
            return Array.isArray(rows) && rows.length > 0 && rows.every((row) => Boolean(this.calculateFriendlyRowWinner(row)));
        },
        async autoSaveFriendlyResults() {
            if (this.friendlyResultDialog.saving) {
                this.friendlyResultDialog.saveAgain = true;
                return;
            }
            do {
                this.friendlyResultDialog.saveAgain = false;
                await this.saveFriendlyResults(false, { closeDialog: false, reloadMatches: false });
            } while (this.friendlyResultDialog.saveAgain);
        },
        async saveFriendlyResults(isCompleted = false, options = {}) {
            const { closeDialog = true, reloadMatches = true } = options;
            const match = this.friendlyResultDialog.match;
            if (!match || this.isFriendlyMatchReadOnly(match)) return;
            for (const row of this.friendlyResultDialog.rows) {
                const normalizedSets = [];
                for (const set of row.sets) {
                    if (!String(set || '').trim()) {
                        normalizedSets.push('');
                        continue;
                    }
                    const normalized = this.normalizeFriendlySetValue(set);
                    if (!normalized) {
                        this.friendlyResultDialog.error = 'Bitte ungültige Sätze korrigieren.';
                        return;
                    }
                    normalizedSets.push(normalized);
                }
                row.sets = normalizedSets;
                this.applyFriendlyRowCompletion(row);
            }
            const score = this.calculateFriendlyResultScore(this.friendlyResultDialog.rows);
            const completed = Boolean(isCompleted || this.isFriendlyResultComplete(this.friendlyResultDialog.rows));
            try {
                this.friendlyResultDialog.saving = true;
                await apiClient.put(`${match.isSharedFriendly ? `/friendly-matches/shared/${this.currentClub}/${match.id}` : `/friendly-matches/${this.currentClub}/${match.id}`}`, {
                    homeMatchPoints: score.home,
                    guestMatchPoints: score.guest,
                    isCompleted: completed,
                    resultDetails: this.friendlyResultDialog.rows
                });
                match.homeMatchPoints = score.home;
                match.guestMatchPoints = score.guest;
                match.isCompleted = completed;
                match.resultDetails = this.friendlyResultDialog.rows.map((row) => ({ ...row, sets: [...row.sets] }));
                if (closeDialog) {
                    this.closeFriendlyResultDialog();
                }
                if (reloadMatches) {
                    await this.loadFriendlyMatches();
                }
            } catch (error) {
                this.friendlyResultDialog.error = getSafeErrorMessage(error, 'Ergebnisse konnten nicht gespeichert werden.');
            } finally {
                this.friendlyResultDialog.saving = false;
            }
        },
        async completeFriendlyResults() {
            await this.saveFriendlyResults(true);
        },
        async saveFriendlyMatch() {
            if (this.friendlyMatchDialog.readonly) return;
            this.friendlyEditDoubleRows();
            try {
                const payload = {
                    ...this.friendlyMatchDialog.form,
                    homeParticipants: this.parseFriendlyArray(this.friendlyMatchDialog.form.homeParticipants).map((participant) => ({ ...participant })),
                    guestParticipants: this.parseFriendlyArray(this.friendlyMatchDialog.form.guestParticipants).map((participant) => ({ ...participant }))
                };
                const id = this.friendlyMatchDialog.editingId;
                if (id) {
                    const existingMatch = this.matches.find((item) => Number(item.id) === Number(id));
                    if (existingMatch?.isSharedFriendly) {
                        await apiClient.put(`/friendly-matches/shared/${this.currentClub}/${id}`, payload);
                    } else {
                        await apiClient.put(`/friendly-matches/${this.currentClub}/${id}`, payload);
                    }
                } else {
                    await apiClient.post(`/friendly-matches/${this.currentClub}`, payload);
                }
                this.closeFriendlyMatchDialog();
                await this.loadFriendlyMatches();
            } catch (error) {
                await this.showInfo('Fehler', getSafeErrorMessage(error, 'Freundschaftsspiel konnte nicht gespeichert werden.'), '', 'error');
            }
        },
        async toggleHomeAway(match) {
            if (!match || !match.id || this.isFriendlyMatchReadOnly(match)) return;
            const originalHome = match.homeTeam ? { ...match.homeTeam } : { name: '' };
            const originalGuest = match.guestTeam ? { ...match.guestTeam } : { name: '' };
            // Optimistic UI update: swap locally
            try {
                if (match.homeTeam && match.guestTeam) {
                    const tmp = match.homeTeam.name;
                    match.homeTeam.name = match.guestTeam.name;
                    match.guestTeam.name = tmp;
                } else {
                    match.homeTeam = { name: originalGuest.name };
                    match.guestTeam = { name: originalHome.name };
                }
                // Trigger reactivity: replace in matches array
                const idx = this.matches.findIndex(m => m.id === match.id);
                if (idx !== -1) this.matches.splice(idx, 1, { ...match });

                const payload = {
                    homeTeamName: match.homeTeam?.name || '',
                    guestTeamName: match.guestTeam?.name || ''
                };
                await apiClient.put(`${match.isSharedFriendly ? `/friendly-matches/shared/${this.currentClub}/${match.id}` : `/friendly-matches/${this.currentClub}/${match.id}`}`, payload);
            } catch (error) {
                console.error('toggleHomeAway error:', error);
                // Revert optimistic change
                const idx = this.matches.findIndex(m => m.id === match.id);
                if (idx !== -1) {
                    const reverted = { ...match, homeTeam: originalHome, guestTeam: originalGuest };
                    this.matches.splice(idx, 1, reverted);
                }
                await this.showInfo(this.$t('messages.error'), getSafeErrorMessage(error, this.$t('schedule.swapFailed') || 'Heim/Auswärts konnte nicht getauscht werden.'), '', 'error');
            }
        },
        async deleteFriendlyMatch() {
            if (!this.friendlyMatchDialog.editingId || this.friendlyMatchDialog.readonly) return;
            const confirmed = await this.showConfirm('Freundschaftsspiel löschen', 'Soll dieses Freundschaftsspiel gelöscht werden?', '', 'warning');
            if (!confirmed) return;
            try {
                const target = this.matches.find((item) => Number(item.id) === Number(this.friendlyMatchDialog.editingId));
                if (target?.isSharedFriendly) {
                    await apiClient.delete(`/friendly-matches/shared/${this.currentClub}/${this.friendlyMatchDialog.editingId}`);
                } else {
                    await apiClient.delete(`/friendly-matches/${this.currentClub}/${this.friendlyMatchDialog.editingId}`);
                }
                this.closeFriendlyMatchDialog();
                await this.loadFriendlyMatches();
            } catch (error) {
                await this.showInfo('Fehler', getSafeErrorMessage(error, 'Freundschaftsspiel konnte nicht gelöscht werden.'), '', 'error');
            }
        },
        async importCSV() {
            if (!this.selectedFile) return;

            const formData = new FormData();
            formData.append('file', this.selectedFile);
            formData.append('clubId', this.currentClub);

            try {
                // Let the browser add the multipart boundary. Setting Content-Type manually
                // drops it and prevents Multer from reading form fields such as clubId.
                const response = await apiClient.post(
                    `/matches/import?clubId=${encodeURIComponent(this.currentClub)}`,
                    formData
                );
                if (response.status < 200 || response.status >= 300) {
                    throw new Error(response.data?.error || 'Failed to import CSV data');
                }
                this.showInfo(this.$t('messages.success'), this.$t('schedule.scheduleImportSuccess'), '', 'success');
                this.closeImportModal();
                this.loadTeams();
            } catch (error) {
                this.showInfo(
                    this.$t('messages.error'),
                    getSafeErrorMessage(error, this.$t('schedule.errorImportingCSV')),
                    '',
                    'error'
                );
            }
        },
        // Sortierfunktion für Ligen
        sortLeagues(leagues) {
            if (!Array.isArray(leagues)) {
                return [];
            }
            // Ligen-Priorität
            const leagueOrder = [
                '1. Bundesliga',
                '2. Bundesliga',
                '3. Bundesliga',
                'Regionalliga',
                'Oberliga',
                'Verbandsliga (Hessen)',
                'Bezirksoberliga',
                'Bezirksliga',
                'Bezirksklasse',
                'Kreisliga',
                '1. Kreisklasse',
                '2. Kreisklasse',
                '3. Kreisklasse',
            ];

            // Hilfsfunktionen
            function getLeagueIndex(name) {
                for (let i = 0; i < leagueOrder.length; i++) {
                    if (name.includes(leagueOrder[i])) return i;
                }
                return leagueOrder.length;
            }
            function parseYouth(name) {
                // Gibt {type: 'J'|'M'|'Jugend'|null, age: Zahl|null} zurück
                const m = name.match(/([JM])(\d{1,2})/i);
                if (m) return { type: m[1].toUpperCase(), age: parseInt(m[2]) };
                if (/jugend/i.test(name)) return { type: 'Jugend', age: null };
                return { type: null, age: null };
            }

            // Sortierlogik
            return leagues.slice().sort((a, b) => {
                const ya = parseYouth(a.name);
                const yb = parseYouth(b.name);
                // Erwachsene zuerst
                if (!ya.type && yb.type) return -1;
                if (ya.type && !yb.type) return 1;
                if (!ya.type && !yb.type) {
                    // Beide Erwachsene: nach Liga
                    return getLeagueIndex(a.name) - getLeagueIndex(b.name);
                }
                // Beide Jugend: erst nach Alter aufsteigend (älteste unten), dann J vor M vor "Jugend", dann Liga
                // "Jugend" ohne Zahl ist die jüngste Jugendklasse
                if (ya.age !== yb.age) {
                    if (ya.age === null) return -1;
                    if (yb.age === null) return 1;
                    return ya.age - yb.age;
                }
                // Reihenfolge: J < M < Jugend
                const typeOrder = { 'J': 0, 'M': 1, 'Jugend': 2 };
                if (ya.type !== yb.type) return (typeOrder[ya.type] || 2) - (typeOrder[yb.type] || 2);
                return getLeagueIndex(a.name) - getLeagueIndex(b.name);
            });
        },

        async loadTeams() {
            try {
                const clubId = this.currentClub;
                const seasonParam = this.selectedSeasonId ? `?seasonid=${this.selectedSeasonId}` : '';
                const response = await apiClient.get(`/club-teams/club/${clubId}${seasonParam}`);
                const teams = response.data.filter(team => !!team.league);

                const uniqueLeaguesMap = new Map();
                teams.forEach(team => {
                    if (team.league && !uniqueLeaguesMap.has(team.league.id)) {
                        uniqueLeaguesMap.set(team.league.id, team.league);
                    }
                });
                const sortedLeagues = this.sortLeagues(Array.from(uniqueLeaguesMap.values()));
                const leagueOrder = sortedLeagues.map(l => l.id);

                this.teams = teams.sort((a, b) => {
                    const idxA = leagueOrder.indexOf(a.league.id);
                    const idxB = leagueOrder.indexOf(b.league.id);
                    if (idxA !== idxB) return idxA - idxB;
                    return a.name.localeCompare(b.name);
                });

                if (!this.selectedLeague && this.teams.length > 0) {
                    await this.loadMatchesForSpecificTeam(this.teams[0], { resetScope: true });
                }
            } catch (error) {
                console.error('ScheduleView: Error loading teams:', error);
                this.showInfo(this.$t('messages.error'), this.$t('schedule.errorLoadingTeams'), '', 'error');
            }
        },
        onSeasonChange(season) {
            this.currentSeason = season;
            this.loadTeams();
            // Leere die aktuellen Matches, da sie für eine andere Saison sind
            this.matches = [];
            this.selectedLeague = '';
            this.selectedTeam = null;
        },
        async loadMatchesForTeam(team) {
            await this.loadMatchesForSpecificTeam(team, { resetScope: true });
        },
        async loadMatchesForSpecificTeam(team, { resetScope = false } = {}) {
            if (!team || !team.league) {
                this.showInfo(this.$t('messages.warning'), this.$t('schedule.noLeagueForTeam'), '', 'warning');
                return;
            }
            this.selectedTeam = team;
            this.selectedLeague = `${team.name}${team.league?.name ? ` (${team.league.name})` : ''}`;
            this.activeTab = 'schedule';
            if (resetScope) {
                this.leagueMatchScope = 'own';
                this.selectedComparisonTeamName = '';
            }
            try {
                const ownResponse = await apiClient.get(`/matches/leagues/${this.currentClub}/matches/${team.league.id}`);
                this.ownLeagueMatches = this.filterRegularScheduleMatches(ownResponse.data);
                await this.loadLeagueMatches(team.league.id);
                this.applyLeagueMatchScope();
                // Lade auch die Tabellendaten für diese Liga
                await this.loadLeagueTable(team.league.id);
                await this.$nextTick();
                if (typeof window !== 'undefined' && window.innerWidth <= 960) {
                    const workspaceHeader = this.$refs.scheduleShell?.$el?.querySelector('.schedule-workspace-header');
                    workspaceHeader?.scrollIntoView({ behavior: 'smooth', block: 'start' });
                }
            } catch (error) {
                this.showInfo(this.$t('messages.error'), this.$t('schedule.errorLoadingMatches'), '', 'error');
                this.matches = [];
                this.ownLeagueMatches = [];
                this.allLeagueMatches = [];
            }
        },
        async loadLeagueMatches(leagueId) {
            const response = await apiClient.get(`/matches/leagues/${this.currentClub}/matches/${leagueId}?scope=all`);
            this.allLeagueMatches = this.filterRegularScheduleMatches(response.data);
        },
        getCombinedLeagueMatches() {
            const combined = [...(this.allLeagueMatches || []), ...(this.ownLeagueMatches || [])];
            const seenIds = new Set();

            const uniqueMatches = combined.filter((match) => {
                const key = match?.id ?? `${match?.date || ''}-${match?.time || ''}-${match?.homeTeam?.name || ''}-${match?.guestTeam?.name || ''}`;
                if (seenIds.has(key)) {
                    return false;
                }
                seenIds.add(key);
                return true;
            });

            return this.sortMatchesByDateTime(uniqueMatches);
        },
        applyLeagueMatchScope() {
            if (!this.selectedTeam) {
                return;
            }
            const ownTeamName = this.selectedTeam.name;
            const combinedLeagueMatches = this.getCombinedLeagueMatches();
            if (this.leagueMatchScope === 'all') {
                this.matches = combinedLeagueMatches;
                return;
            }
            if (this.leagueMatchScope === 'other') {
                if (!this.selectedComparisonTeamName && this.leagueTeamOptions.length > 0) {
                    this.selectedComparisonTeamName = this.leagueTeamOptions[0];
                }
                this.matches = this.selectedComparisonTeamName
                    ? combinedLeagueMatches.filter(match =>
                        match.homeTeam?.name === this.selectedComparisonTeamName ||
                        match.guestTeam?.name === this.selectedComparisonTeamName
                    )
                    : [];
                return;
            }
            this.matches = this.ownLeagueMatches.length > 0
                ? this.sortMatchesByDateTime(this.ownLeagueMatches)
                : combinedLeagueMatches.filter(match =>
                    match.homeTeam?.name === ownTeamName || match.guestTeam?.name === ownTeamName
                );
        },
        setLeagueMatchScope(scope) {
            this.leagueMatchScope = scope;
            if (scope !== 'other') {
                this.selectedComparisonTeamName = '';
            }
            this.applyLeagueMatchScope();
        },
        async loadAllMatches() {
            this.selectedLeague = this.$t('schedule.overallSchedule');
            this.selectedTeam = null;
            this.ownLeagueMatches = [];
            this.allLeagueMatches = [];
            this.leagueMatchScope = 'own';
            this.selectedComparisonTeamName = '';
            try {
                const seasonParam = this.selectedSeasonId ? `?seasonid=${this.selectedSeasonId}` : '';
                const response = await apiClient.get(`/matches/leagues/${this.currentClub}/matches${seasonParam}`);
                this.matches = this.sortMatchesByDateTime(this.filterRegularScheduleMatches(response.data));
            } catch (error) {
                this.showInfo(this.$t('messages.error'), this.$t('schedule.errorLoadingOverallSchedule'), '', 'error');
                this.matches = [];
            }
        },
        async loadAdultMatches() {
            this.selectedLeague = this.$t('schedule.adultSchedule');
            this.selectedTeam = null;
            this.ownLeagueMatches = [];
            this.allLeagueMatches = [];
            this.leagueMatchScope = 'own';
            this.selectedComparisonTeamName = '';
            try {
                const seasonParam = this.selectedSeasonId ? `?seasonid=${this.selectedSeasonId}` : '';
                const response = await apiClient.get(`/matches/leagues/${this.currentClub}/matches${seasonParam}`);
                // Filtere nur Erwachsenenligen (keine Jugendligen)
                const allMatches = this.filterRegularScheduleMatches(response.data);
                this.matches = this.sortMatchesByDateTime(allMatches.filter(match => {
                    const leagueName = match.leagueDetails?.name || '';
                    // Prüfe, ob es eine Jugendliga ist (J, M, Jugend im Namen)
                    const isYouth = /[JM]\d|jugend/i.test(leagueName);
                    return !isYouth;
                }));
            } catch (error) {
                this.showInfo(this.$t('messages.error'), this.$t('schedule.errorLoadingAdultSchedule'), '', 'error');
                this.matches = [];
            }
        },
        async loadFriendlyMatches() {
            this.selectedLeague = this.friendlyMatchesLabel;
            this.selectedTeam = null;
            this.ownLeagueMatches = [];
            this.allLeagueMatches = [];
            this.leagueMatchScope = 'own';
            this.selectedComparisonTeamName = '';
            this.activeTab = 'schedule';
            this.leagueTable = [];
            try {
                const [localResponse, sharedResponse] = await Promise.all([
                    apiClient.get(`/friendly-matches/${this.currentClub}`),
                    apiClient.get(`/friendly-matches/shared/${this.currentClub}`),
                ]);
                this.friendlyMatches = [...(localResponse.data || []), ...(sharedResponse.data || [])];
                this.matches = this.sortMatchesByDateTime(this.friendlyMatches);
            } catch (error) {
                this.showInfo(this.$t('messages.error'), getSafeErrorMessage(error, 'Freundschaftsspiele konnten nicht geladen werden.'), '', 'error');
                this.friendlyMatches = [];
                this.matches = [];
            }
        },
        formatDate(date) {
            if (!date) return 'N/A';
            const d = new Date(date);
            if (isNaN(d.getTime())) return 'N/A';
            const weekdays = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'];
            const wd = weekdays[d.getDay()];
            const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
            const day = d.toLocaleDateString('de-DE', options);
            return `${wd} ${day}`;
        },
        isClubHighlighted(teamName) {
            if (!teamName) return false;
            const clubName = this.currentClubName;
            return Boolean(clubName && teamName.includes(clubName));
        },
        getCurrentClubName() {
            const clubIdNum = Number(this.currentClub);
            const club = this.clubs.find(c => c.id === clubIdNum);
            return club ? club.name : '';
        },
        async generatePDF() {
            const element = this.$refs.scheduleShell?.$el || this.$refs.scheduleContainer;
            const highlightName = this.getCurrentClubName();
            if (element) {
                const pdfGen = new PDFGenerator(20, 10, this.$t);
                pdfGen.addTitle(`${this.$t('schedule.gamesFor')} ${highlightName} ${this.$t('common.in')} ${this.selectedLeague}`);
                
                // Die Ortsliste steht auf Seite 2. In der Spieltabelle bleiben Datum,
                // Uhrzeit, Heim- und Gastmannschaft sowie ggf. die Altersklasse.
                // Spaltenstruktur: Ort, Datum, Uhrzeit, Heim, Gast, Ergebnis,
                // [Altersklasse], Code, Heim-PIN, Gast-PIN
                const hasAgeClass = this.selectedLeague === this.$t('schedule.overallSchedule') || this.selectedLeague === this.$t('schedule.adultSchedule');
                let excludeColumns;
                if (hasAgeClass) {
                    // Mit Altersklasse: Ort=0, Ergebnis=5, Code=7, Heim-PIN=8, Gast-PIN=9
                    excludeColumns = [0, 5, 7, 8, 9];
                } else {
                    // Ohne Altersklasse: Ort=0, Ergebnis=5, Code=6, Heim-PIN=7, Gast-PIN=8
                    excludeColumns = [0, 5, 6, 7, 8];
                }
                
                const homePinIndex = hasAgeClass ? 8 : 7;
                const guestPinIndex = hasAgeClass ? 9 : 8;
                const getOwnTeamPin = (cells, index) => {
                    if (this.friendlyOnly || (index !== 3 && index !== 4)) return null;
                    const teamName = cells[index]?.text || '';
                    const pin = cells[index === 3 ? homePinIndex : guestPinIndex]?.text || '';
                    return teamName.includes(highlightName) && pin !== '-' ? pin : null;
                };
                pdfGen.addTable('schedule-table', highlightName, excludeColumns, getOwnTeamPin);
                pdfGen.addNewPage();
                const uniqueLocations = this.getUniqueLocations();
                uniqueLocations.forEach((addressLines, clubName) => {
                    if (!clubName.includes(highlightName)) {
                        pdfGen.addAddress(clubName, addressLines);
                    }
                });

                pdfGen.save(this.$t('schedule.schedulePDF'));
            } else {
                await this.showInfo(this.$t('messages.info'), this.$t('schedule.noMatchesForPDF'), '', 'info');
            }
        },
        getUniqueLocations() {
            const uniqueLocations = new Map();
            this.matches.forEach(match => {
                if (!match.location || !match.homeTeam) return;
                const location = match.location;
                const clubName = match.homeTeam.name;
                const addressLines = [
                    location.name || 'N/A',
                    location.address || 'N/A',
                    `${location.zip || ''} ${location.city || ''}`.trim()
                ];
                const addressKey = addressLines.join('; ');
                if (!uniqueLocations.has(addressKey)) {
                    uniqueLocations.set(clubName, addressLines);
                }
            });

            return uniqueLocations;
        },
        getRowClass(matchDate) {
            if (!matchDate) return '';

            const today = new Date();
            const match = new Date(matchDate);

            // Setze die Zeit auf Mitternacht für genaue Datumsvergleiche
            today.setHours(0, 0, 0, 0);
            match.setHours(0, 0, 0, 0);

            // Berechne die Differenz in Tagen
            const diffTime = match.getTime() - today.getTime();
            const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

            if (diffDays === 0) {
                return 'match-today'; // Heute - gelb
            } else if (diffDays > 0 && diffDays <= 7) {
                return 'match-next-week'; // Nächste Woche - hellblau
            }

            return ''; // Keine besondere Farbe
        },
        async copyToClipboard(text, type, event) {
            try {
                await navigator.clipboard.writeText(text);
                // Zeige eine kurze Bestätigung
                this.showCopyFeedback(event.target, text);
            } catch (err) {
                console.error('Fehler beim Kopieren:', err);
                // Fallback für ältere Browser
                const textArea = document.createElement('textarea');
                textArea.value = text;
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
                
                // Auch für Fallback eine Bestätigung zeigen
                this.showCopyFeedback(event.target, text);
            }
        },
        
        showCopyFeedback(element, originalText) {
            // Speichere die ursprünglichen Styles
            const originalStyles = {
                textContent: element.textContent,
                color: element.style.color,
                backgroundColor: element.style.backgroundColor,
                borderColor: element.style.borderColor,
                fontWeight: element.style.fontWeight
            };
            
            // Ändere das Element zu einem grünen Häkchen
            element.textContent = '✓';
            element.style.color = '#4CAF50';
            element.style.backgroundColor = '#e8f5e8';
            element.style.borderColor = '#4CAF50';
            element.style.fontWeight = 'bold';
            element.style.fontSize = '1.2em';
            
            // Nach 1.5 Sekunden zurücksetzen
            setTimeout(() => {
                element.textContent = originalStyles.textContent;
                element.style.color = originalStyles.color;
                element.style.backgroundColor = originalStyles.backgroundColor;
                element.style.borderColor = originalStyles.borderColor;
                element.style.fontWeight = originalStyles.fontWeight;
                element.style.fontSize = '';
            }, 1500);
        },

        openMatchReport(match) {
            if (!match || !match.code) {
                return;
            }
            const title = `${match.homeTeam?.name || 'N/A'} vs ${match.guestTeam?.name || 'N/A'} - ${this.selectedLeague}`;
            this.openDialog({
                title,
                component: 'MatchReportApiDialog',
                props: {
                    match
                }
            });
        },
        
        async loadLeagueTable(leagueId) {
            try {
                const response = await apiClient.get(`/matches/leagues/${this.currentClub}/table/${leagueId}`);
                this.leagueTable = response.data;
            } catch (error) {
                console.error('ScheduleView: Error loading league table:', error);
                this.leagueTable = [];
            }
        },
        
        async fetchTableFromMyTischtennis() {
            if (!this.selectedTeam || !this.selectedTeam.league || this.selectedLeague === this.$t('schedule.overallSchedule') || this.selectedLeague === this.$t('schedule.adultSchedule')) {
                this.showInfo(this.$t('messages.info'), this.$t('schedule.selectSpecificLeague'), '', 'info');
                return;
            }
            
            this.fetchingTable = true;
            try {
                const leagueId = this.selectedTeam.league.id;
                const response = await apiClient.post(`/matches/leagues/${this.currentClub}/table/${leagueId}/fetch`);
                this.leagueTable = response.data.data;
                this.showInfo(this.$t('messages.success'), this.$t('schedule.tableDataLoaded'), '', 'success');
            } catch (error) {
                console.error('ScheduleView: Error fetching table from MyTischtennis:', error);
                this.showInfo(this.$t('messages.error'), this.$t('schedule.errorLoadingTable'), getSafeErrorMessage(error), 'error');
            } finally {
                this.fetchingTable = false;
            }
        },
        async fetchTeamDataManually() {
            if (!this.selectedTeam?.id) {
                return;
            }

            this.fetchingTeamData = true;
            try {
                const response = await apiClient.post('/scheduler/match_results', {
                    clubTeamId: this.selectedTeam.id
                });
                const result = response?.data;

                if (!result?.success) {
                    throw new Error(result?.error || result?.message || this.$t('schedule.fetchDataFailed'));
                }

                await this.loadMatchesForSpecificTeam(this.selectedTeam);
                if (this.selectedTeam?.league?.id) {
                    await this.loadLeagueTable(this.selectedTeam.league.id);
                }

                const fetchedCount =
                    result?.result?.totalFetched ??
                    result?.result?.fetchedCount ??
                    result?.result?.data?.fetchedCount ??
                    0;

                await this.showInfo(
                    this.$t('messages.success'),
                    this.$t('schedule.teamDataFetched'),
                    this.$t('schedule.teamDataFetchedDetails', {
                        team: this.selectedTeam.name,
                        count: fetchedCount
                    }),
                    'success'
                );
            } catch (error) {
                await this.showInfo(
                    this.$t('messages.error'),
                    getSafeErrorMessage(error, this.$t('schedule.fetchDataFailed')),
                    '',
                    'error'
                );
            } finally {
                this.fetchingTeamData = false;
            }
        },

        refreshScheduleData() {
            if (!this.selectedLeague) return;
            if (this.selectedTeam) {
                this.loadMatchesForSpecificTeam(this.selectedTeam);
            } else if (this.selectedLeague === this.$t('schedule.overallSchedule')) {
                this.loadAllMatches();
            } else if (this.selectedLeague === this.$t('schedule.adultSchedule')) {
                this.loadAdultMatches();
            } else if (this.selectedLeague === this.friendlyMatchesLabel) {
                this.loadFriendlyMatches();
            }
        },

        handleScheduleMatchUpdated(payload) {
            if (this.friendlyOnly) {
                if (payload?.match?.isFriendly) {
                    const idx = this.matches.findIndex(m => m.id === payload.match.id);
                    if (idx !== -1) {
                        this.matches.splice(idx, 1, payload.match);
                    } else {
                        this.matches.push(payload.match);
                    }
                    this.friendlyMatches = [...this.matches];
                    this.matches = this.sortMatchesByDateTime(this.matches);
                    return;
                }
                if (payload?.matchId != null) {
                    const idx = this.matches.findIndex(m => m.id === payload.matchId);
                    if (idx !== -1) {
                        this.matches.splice(idx, 1);
                        this.friendlyMatches = [...this.matches];
                        return;
                    }
                }
                this.refreshScheduleData();
                return;
            }
            if (payload?.match && payload.matchId != null) {
                if (payload.match.isFriendly) {
                    this.matches = this.matches.filter((match) => match.id !== payload.matchId);
                    return;
                }
                const idx = this.matches.findIndex(m => m.id === payload.matchId);
                if (idx !== -1) {
                    this.matches.splice(idx, 1, payload.match);
                    this.matches = this.sortMatchesByDateTime(this.matches);
                    return;
                }
            }
            this.refreshScheduleData();
        },

        handleMatchReportSubmitted(payload) {
            if (payload?.matchData && (payload.matchCode != null || payload.matchData?.gameCode || payload.matchData?.code)) {
                const code = String(payload.matchCode ?? payload.matchData?.gameCode ?? payload.matchData?.code ?? '');
                const idx = this.matches.findIndex(m => m.code === code);
                if (idx !== -1) {
                    const m = { ...this.matches[idx] };
                    const d = payload.matchData;
                    if (d.homeMatches != null) m.homeMatchPoints = d.homeMatches;
                    if (d.guestMatches != null) m.guestMatchPoints = d.guestMatches;
                    if (typeof d.isCompleted === 'boolean') m.isCompleted = d.isCompleted;
                    if (d.startDate) m.startDate = d.startDate;
                    if (d.endDate) m.endDate = d.endDate;
                    this.matches.splice(idx, 1, m);
                    this.matches = this.sortMatchesByDateTime(this.matches);
                    return;
                }
            }
            this.refreshScheduleData();
        },
        handleFriendlyInvitationRealtime() {
            if (!this.friendlyOnly) return;
            this.loadFriendlyInvitations();
        },
        handleFriendlySharedMatchUpdatedRealtime(payload) {
            if (!this.friendlyOnly || !payload?.match) return;
            const idx = this.matches.findIndex((match) => match.id === payload.match.id);
            if (idx !== -1) {
                this.matches.splice(idx, 1, payload.match);
            } else {
                this.matches.push(payload.match);
            }
            this.friendlyMatches = [...this.matches];
            this.matches = this.sortMatchesByDateTime(this.matches);
        },
        handleFriendlySharedMatchDeletedRealtime(payload) {
            if (!this.friendlyOnly || payload?.matchId == null) return;
            this.matches = this.matches.filter((match) => match.id !== payload.matchId);
            this.friendlyMatches = [...this.matches];
        },
    },
    async created() {
        if (this.friendlyOnly) {
            await Promise.all([this.loadFriendlyMatches(), this.loadFriendlyInvitations()]);
            return;
        }
        this.loadTeams();
    },
    beforeUnmount() {
        offScheduleMatchUpdated(this.handleScheduleMatchUpdated);
        offMatchReportSubmitted(this.handleMatchReportSubmitted);
        offFriendlyInvitationCreated(this.handleFriendlyInvitationRealtime);
        offFriendlyInvitationAccepted(this.handleFriendlyInvitationRealtime);
        offFriendlyInvitationDeclined(this.handleFriendlyInvitationRealtime);
        offFriendlySharedMatchUpdated(this.handleFriendlySharedMatchUpdatedRealtime);
        offFriendlySharedMatchDeleted(this.handleFriendlySharedMatchDeletedRealtime);
        disconnectSocket();
    },
};
</script>

<style scoped>
.output {
    display: flex;
}

.output ul {
    flex: 0;
    margin-right: 20px;
}

.flex-item {
    flex: 1;
}

table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 20px;
}

table,
th,
td {
    border: 1px solid #ddd;
    padding: 8px;
}

th {
    background-color: #f2f2f2;
    text-align: left;
}

tr:hover {
    background-color: #f5f5f5;
}

th,
td {
    white-space: nowrap;
}

.highlighted-club {
    font-weight: 600;
    color: #2b7cff;
}

.result-cell {
    text-align: center;
    font-weight: 600;
}

.result-score {
    font-size: 1.1em;
}

.result-pending {
    color: var(--text-muted);
    font-style: italic;
}

.result-cell.completed.won {
    background-color: #f0f9f0;
}

.result-cell.completed.won .result-score {
    color: #28a745;
}

.result-cell.completed.lost {
    background-color: #fff5f5;
}

.result-cell.completed.lost .result-score {
    color: #dc3545;
}

/* Unentschieden: gelb hinterlegen (wie .row-test in MembersView) */
.result-cell.completed.draw {
    background-color: #fff3cd;
}

.result-cell.completed.draw .result-score {
    color: #856404;
}

.friendly-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.friendly-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.75rem;
}

.friendly-form-grid label,
.friendly-participant-column {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.friendly-form-grid input,
.friendly-form-grid select,
.friendly-add-row input,
.friendly-add-row select {
    width: 100%;
    box-sizing: border-box;
    padding: 0.45rem 0.55rem;
    border: 1px solid var(--border-color, #ddd);
    border-radius: 6px;
}

.friendly-venue-summary {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    color: #374151;
    font-size: 0.9rem;
}

.friendly-checkbox {
    flex-direction: row !important;
    align-items: center;
}

.friendly-participants {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
}

.friendly-participant-column {
    border: 1px solid var(--border-color, #ddd);
    border-radius: 8px;
    padding: 0.75rem;
}

.friendly-add-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.5rem;
}

.friendly-participant-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.friendly-participant-list li {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.35rem 0.5rem;
    background: var(--background-soft, #f7f7f7);
    border-radius: 6px;
}

.friendly-doubles-section {
    border: 1px solid var(--border-color, #ddd);
    border-radius: 8px;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.friendly-double-row {
    display: grid;
    gap: 0.5rem;
    background: var(--background-soft, #f7f7f7);
    border-radius: 6px;
    padding: 0.65rem;
}

.friendly-double-side {
    display: grid;
    grid-template-columns: 4rem 1fr 1fr;
    gap: 0.5rem;
    align-items: center;
}

.friendly-double-side select {
    min-width: 0;
}

.friendly-actions-cell {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

.friendly-result-dialog {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.friendly-result-table {
    width: 100%;
    border-collapse: collapse;
}

.friendly-result-table input,
.friendly-result-table select {
    width: 100%;
    box-sizing: border-box;
    padding: 0.35rem 0.45rem;
    border: 1px solid var(--border-color, #ddd);
    border-radius: 4px;
}

.friendly-result-table .player-input {
    min-width: 12rem;
}

.friendly-double-select {
    display: grid;
    gap: 0.25rem;
}

.friendly-result-table .set-input {
    width: 4.5rem;
    text-align: center;
}

.friendly-result-score-cell,
.score-value {
    border-radius: 4px;
    padding: 0.2rem 0.45rem;
}

.score-leading {
    color: #0f7a3b;
    background: #e8f7ee;
}

.score-trailing {
    color: #b42318;
    background: #fff0ee;
}

.score-even {
    color: #175cd3;
    background: #eef4ff;
}

.friendly-result-error {
    color: #b00020;
    font-weight: 600;
}

.friendly-invitations-card {
    border: 1px solid #dbe3ea;
    border-radius: 10px;
    background: #f9fcff;
    padding: 0.85rem;
    margin-bottom: 1rem;
}

.friendly-invitations-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
}

.friendly-invitations-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
    margin-top: 0.75rem;
}

.friendly-invitation-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}

.friendly-invitation-list li {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.55rem 0.6rem;
    border: 1px solid #e5ecf2;
    border-radius: 8px;
    background: #fff;
}

.friendly-invitation-main {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.friendly-invitation-actions {
    display: flex;
    gap: 0.4rem;
}

.friendly-invitation-empty {
    color: #64748b;
    margin: 0.4rem 0 0;
}

.friendly-invitation-status {
    align-self: center;
    color: #475569;
}

.friendly-invitation-form {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
}

.friendly-invitation-message {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.friendly-invitation-message textarea {
    width: 100%;
    box-sizing: border-box;
    padding: 0.45rem 0.55rem;
    border: 1px solid var(--border-color, #ddd);
    border-radius: 6px;
    resize: vertical;
}

.modal {
    display: flex;
    justify-content: center;
    align-items: center;
    position: fixed;
    z-index: 1000;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: rgba(0, 0, 0, 0.8);
}

.modal-content {
    background-color: #fefefe;
    padding: 20px;
    border: 1px solid #888;
    width: 80%;
    max-width: 500px;
    max-height: 80%;
    box-shadow: 4px 3px 2px #999;
    position: relative;
}

.close {
    position: absolute;
    top: 10px;
    right: 15px;
    color: #aaa;
    font-size: 28px;
    font-weight: bold;
}

.close:hover,
.close:focus {
    color: #000;
    text-decoration: none;
    cursor: pointer;
}

ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
}

li {
    white-space: nowrap;
    color: #45a049;
    cursor: pointer;
}

.special-link {
    font-weight: bold;
    color: #2c5aa0 !important;
    padding: 5px 0;
    border-bottom: 1px solid #ddd;
    margin-bottom: 5px;
}

.special-link:hover {
    background-color: #f0f8ff;
    padding-left: 5px;
    transition: all 0.3s ease;
}

.no-leagues {
    color: #666;
    font-style: italic;
    padding: 0.5rem;
}

.divider {
    height: 1px;
    background-color: #ddd;
    margin: 10px 0;
    cursor: default !important;
    color: transparent !important;
}

/* Code und PIN Styles */
.code-cell,
.pin-cell {
    text-align: center;
    font-family: 'Courier New', monospace;
    font-weight: bold;
    min-width: 80px;
}

.match-report-cell {
    cursor: pointer;
}

.code-value {
    background: #e3f2fd;
    color: #1976d2;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 0.9rem;
    display: inline-block;
    border: 1px solid #bbdefb;
}

.code-value.clickable {
    cursor: pointer;
    transition: all 0.2s ease;
}

.code-value.clickable:hover {
    background: #bbdefb;
    transform: scale(1.05);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.nuscore-link {
    text-decoration: none;
    margin-right: 8px;
    font-size: 1.2rem;
    transition: transform 0.2s ease;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    color: inherit;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 999px;
    background-color: #e3f2fd;
    border: 1px solid #bbdefb;
}

.nuscore-link:hover {
    transform: scale(1.2);
}

@media (pointer: coarse), (max-width: 900px) {
    .nuscore-link {
        width: 2.75rem;
        height: 2.75rem;
        font-size: 1.4rem;
    }
}

.pin-value {
    background: #fff3e0;
    color: #f57c00;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 0.9rem;
    display: inline-block;
    border: 1px solid #ffcc02;
}

.pin-value.clickable {
    cursor: pointer;
    transition: all 0.2s ease;
}

.pin-value.clickable:hover {
    background: #ffcc02;
    transform: scale(1.05);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.no-data {
    color: #999;
    font-style: italic;
}

.match-today {
    background-color: #fff3cd !important;
    /* Gelb für heute (wie .row-test in MembersView) */
}

.match-next-week {
    background-color: #ffffcc !important;
    /* Hellgelb für nächste Woche (wie .row-test-form in MembersView) */
}

.match-today:hover {
    background-color: #ffeaa7 !important;
    /* Dunkleres Gelb beim Hover */
}

.match-next-week:hover {
    background-color: #fff9cc !important;
    /* Dunkleres Gelb beim Hover */
}

/* Tab Navigation */
.tab-navigation {
    display: flex;
    gap: 0;
    border-bottom: 2px solid #e0e0e0;
    margin-bottom: 20px;
}

.tab-button {
    background: none;
    border: none;
    padding: 12px 24px;
    font-size: 16px;
    font-weight: 500;
    color: #666;
    cursor: pointer;
    border-bottom: 3px solid transparent;
    transition: all 0.3s ease;
    margin-bottom: -2px;
}

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

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

/* Tab Content */
.tab-content {
    display: block !important; /* Überschreibe globales display: none */
    width: 100%;
}

.tab-panel {
    width: 100%;
}

/* Table Section */
.table-section {
    padding: 0;
}

.table-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}

.table-header h3 {
    margin: 0;
}

.schedule-table-wrapper {
    width: 100%;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    touch-action: pan-x pan-y;
}

#schedule-table {
    width: 100%;
    min-width: 980px;
    border-collapse: collapse;
}

#schedule-table th,
#schedule-table td {
    padding: 12px 8px;
    text-align: left;
    border: 1px solid #ddd;
    white-space: nowrap;
}

#schedule-table th {
    background-color: #f8f9fa;
    font-weight: 600;
}

#schedule-table tr:hover {
    background-color: #f5f5f5;
}

.fetch-table-btn {
    background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
    color: var(--text-on-primary);
    border: 1px solid transparent;
    padding: 8px 16px;
    border-radius: 10px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 600;
    transition: transform 0.2s ease, opacity 0.2s ease;
}

.fetch-table-btn:hover:not(:disabled) {
    transform: translateY(-1px);
    opacity: 0.95;
}

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

#league-table {
    width: 100%;
    min-width: 640px;
    border-collapse: collapse;
}

#league-table th,
#league-table td {
    padding: 12px 8px;
    text-align: left;
    border: 1px solid #ddd;
}

#league-table th {
    background-color: #f8f9fa;
    font-weight: 600;
}

#league-table tr:hover {
    background-color: #f5f5f5;
}

#league-table tr.our-team {
    background-color: #e8f5e8;
    font-weight: 600;
}

#league-table tr.our-team:hover {
    background-color: #d4edda;
}

/* Player Selection Dialog */
.player-selection-content {
    padding: 10px 0;
}

.match-info {
    margin-bottom: 20px;
    padding: 10px;
    background-color: #f8f9fa;
    border-radius: 4px;
}

.match-info p {
    margin: 5px 0;
}

.loading-state {
    padding: 40px;
    text-align: center;
    color: #6c757d;
}

.player-selection-table {
    width: 100%;
    border-collapse: collapse;
    margin: 20px 0;
    min-width: 480px;
}

.player-selection-table th,
.player-selection-table td {
    padding: 8px 12px;
    text-align: left;
    border-bottom: 1px solid #dee2e6;
    white-space: nowrap;
    vertical-align: middle;
}

.player-selection-table th {
    background-color: #f8f9fa;
    font-weight: 600;
    color: #495057;
}

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

.player-name {
    font-weight: 500;
    min-width: 220px;
}

.player-list {
    overflow-x: auto;
}

/* Gallery Styles */
.gallery-dialog-content {
    display: flex;
    flex-direction: column;
    padding: 0;
    min-height: 60vh;
    max-height: 70vh;
    overflow: auto;
}

.gallery-controls {
    width: 100%;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border-color, #ddd);
    display: flex;
    align-items: center;
    gap: 12px;
    background: #f9f9f9;
    flex-shrink: 0;
}

.gallery-controls label {
    font-weight: 500;
    color: var(--text-color, #333);
}

.gallery-controls select {
    padding: 6px 12px;
    border: 1px solid var(--border-color, #ddd);
    border-radius: 4px;
    font-size: 14px;
    background: white;
    cursor: pointer;
}

.gallery-controls select:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.gallery-members-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 16px;
    padding: 16px;
}

.gallery-member-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    cursor: pointer;
    padding: 8px;
    border: 2px solid transparent;
    border-radius: 8px;
    transition: all 0.2s ease;
}

.gallery-member-item:hover {
    background-color: #f0f0f0;
    border-color: #007bff;
}

.gallery-member-item.is-ready {
    border-color: #28a745;
    background-color: #d4edda;
}

.gallery-member-image {
    width: 100%;
    height: auto;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: 4px;
    margin-bottom: 8px;
    pointer-events: none; /* Prevent image from blocking clicks on parent */
}

.gallery-member-name {
    font-size: 12px;
    text-align: center;
    color: var(--text-color, #333);
    font-weight: 500;
}

.gallery-loading,
.gallery-error {
    padding: 20px;
    text-align: center;
    font-size: 1rem;
    color: var(--text-color, #333);
}

.checkbox-cell {
    text-align: center;
    width: 84px;
    min-width: 84px;
}

.checkbox-cell input[type="checkbox"] {
    width: 18px;
    height: 18px;
    cursor: pointer;
}

.no-members {
    padding: 40px;
    text-align: center;
    color: #6c757d;
    font-style: italic;
}

.dialog-actions {
    margin-top: 20px;
    padding-top: 20px;
    border-top: 1px solid #dee2e6;
    display: flex;
    gap: 10px;
    justify-content: flex-end;
}

.btn-save,
.btn-cancel {
    padding: 8px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
    transition: background-color 0.2s;
}

.btn-save {
    background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
    color: var(--text-on-primary);
}

.btn-save:hover {
    transform: translateY(-1px);
}

.btn-cancel {
    background: var(--surface-color);
    color: var(--text-color);
    border-color: var(--border-color);
}

.btn-cancel:hover {
    background-color: #5a6268;
}

.schedule-view {
    display: flex;
    flex-direction: column;
    gap: 16px;
    height: 100%;
    min-height: 0;
    min-width: 0;
    max-width: 100%;
    overflow-x: hidden;
    box-sizing: border-box;
}

.schedule-page-header {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    align-items: flex-start;
    flex-wrap: wrap;
}

.schedule-page-title h2 {
    margin: 0 0 4px;
}

.schedule-page-title p {
    margin: 0;
    color: var(--text-muted, #6c757d);
}

.schedule-page-actions {
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
}

.schedule-summary-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

.schedule-summary-item {
    min-width: 120px;
    padding: 10px 12px;
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 10px;
    background: #fff;
}

.schedule-summary-label {
    display: block;
    font-size: 12px;
    color: var(--text-muted, #6c757d);
    margin-bottom: 4px;
}

.schedule-layout {
    display: grid;
    grid-template-columns: 280px minmax(0, 1fr);
    gap: 16px;
    align-items: start;
    min-width: 0;
    max-width: 100%;
    box-sizing: border-box;
}

.schedule-sidebar {
    min-width: 0;
}

.schedule-sidebar-card,
.schedule-workspace-header,
.schedule-empty-state {
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 12px;
    background: #fff;
}

.schedule-sidebar-card {
    padding: 14px;
}

.schedule-sidebar-header,
.schedule-workspace-header {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    align-items: flex-start;
}

.schedule-sidebar-header h3,
.schedule-workspace-header h3 {
    margin: 0;
}

.schedule-selection-count,
.workspace-eyebrow {
    font-size: 12px;
    color: var(--text-muted, #6c757d);
}

.workspace-eyebrow {
    display: block;
    margin-bottom: 4px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.schedule-workspace-header {
    padding: 14px 16px;
    margin-bottom: 14px;
}

.schedule-workspace-header p {
    margin: 6px 0 0;
    color: var(--text-muted, #6c757d);
}

.schedule-workspace-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.schedule-team-search {
    width: 100%;
    margin-top: 12px;
    margin-bottom: 12px;
    padding: 10px 12px;
    border: 1px solid var(--border-color, #ced4da);
    border-radius: 8px;
}

.schedule-quick-links {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 12px;
}

.schedule-quick-link {
    width: 100%;
    text-align: left;
    padding: 10px 12px;
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 8px;
    background: #fff;
    cursor: pointer;
}

.schedule-quick-link.active,
.schedule-team-list li.active {
    border-color: var(--primary-color, #2b7cff);
    color: var(--primary-color, #2b7cff);
    background: rgba(43, 124, 255, 0.08);
    font-weight: 600;
}

.schedule-team-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.schedule-team-list li {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 12px;
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 8px;
    cursor: pointer;
}

.schedule-team-name {
    font-weight: 600;
}

.league-match-scope-card {
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 14px;
    background: linear-gradient(180deg, rgba(47, 122, 95, 0.08), rgba(47, 122, 95, 0.03));
    padding: 14px;
    margin-bottom: 12px;
    position: sticky;
    top: 12px;
    z-index: 5;
    backdrop-filter: blur(6px);
}

.league-match-scope-header {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 10px;
}

.league-match-scope-header span {
    color: var(--text-muted, #6c757d);
    font-size: 14px;
}

.league-match-scope-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}

.team-filter-chip {
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 999px;
    background: var(--surface-color, #fff);
    color: var(--text-color, #333);
    padding: 8px 12px;
    cursor: pointer;
    font-weight: 600;
}

.team-filter-chip.active {
    border-color: var(--primary-soft, #5f9b82);
    color: var(--primary-strong, #1f5f49);
    background: rgba(47, 122, 95, 0.12);
}

.league-match-scope-select {
    min-width: 220px;
    padding: 8px 10px;
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 10px;
    background: var(--surface-color, #fff);
    color: var(--text-color, #333);
}

.league-match-scope-select:focus {
    outline: none;
    border-color: var(--primary-color, #2f7a5f);
    box-shadow: 0 0 0 3px rgba(47, 122, 95, 0.14);
}

.location-info-cell {
    width: 56px;
    text-align: center;
}

.location-info-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border-radius: 999px;
    border: 1px solid var(--border-color, #dee2e6);
    background: #fff;
    color: var(--primary-strong, #1f5f49);
    box-shadow: none;
    text-shadow: none;
}

.location-info-button::before {
    display: none;
}

.location-info-button:hover {
    background: rgba(47, 122, 95, 0.08);
    border-color: var(--primary-color, #2f7a5f);
    transform: none;
    box-shadow: none;
}

.schedule-location-dialog {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.schedule-location-row {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.schedule-location-label {
    font-size: 12px;
    color: var(--text-muted, #6c757d);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.tab-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    margin-left: 6px;
    border-radius: 999px;
    background: rgba(47, 122, 95, 0.12);
    color: var(--primary-strong, #1f5f49);
    font-size: 12px;
}

.schedule-empty-state {
    padding: 28px;
    text-align: center;
}

.schedule-empty-state h3 {
    margin-top: 0;
    margin-bottom: 8px;
}

.schedule-empty-state p {
    margin: 0;
    color: var(--text-muted, #6c757d);
}

.output ul li.active {
    font-weight: 600;
    color: var(--primary-color, #2b7cff);
}

.team-league {
    color: var(--text-muted, #6c757d);
}

@media (max-width: 960px) {
    .schedule-layout {
        grid-template-columns: 1fr;
    }

    .league-match-scope-card {
        position: static;
        top: auto;
    }
}

@media (max-width: 640px) {
    .player-selection-table {
        min-width: 520px;
    }

    .player-selection-table th,
    .player-selection-table td {
        padding: 10px 12px;
    }

    .schedule-view {
        gap: 12px;
    }

    .league-match-scope-card {
        padding: 12px;
    }

    .league-match-scope-controls {
        flex-direction: column;
        align-items: stretch;
    }

    .team-filter-chip,
    .league-match-scope-select {
        width: 100%;
        min-width: 0;
    }

    .tab-button {
        padding: 10px 14px;
        font-size: 0.95rem;
    }

    .location-info-button {
        width: 38px;
        height: 38px;
    }

    .table-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 10px;
    }

    .dialog-actions {
        flex-direction: column;
    }

    .dialog-actions > button {
        width: 100%;
    }
}
</style>
