<template>
  <div class="official-tournaments">
    <div class="workspace-admin">
      <div class="admin-panel">
        <div class="panel-header">
          <h3>Turnier importieren</h3>
          <p>PDF hochladen und als neues offizielles Turnier anlegen.</p>
        </div>
        <div class="uploader">
          <input type="file" accept="application/pdf" @change="onFile" />
          <button class="btn-primary" :disabled="!selectedFile" @click="upload">{{ $t('officialTournaments.uploadPdf') }}</button>
        </div>
      </div>

      <div class="admin-panel">
        <div class="panel-header">
          <h3>{{ $t('officialTournaments.savedEvents') }}</h3>
          <p>Turnier auswählen, umbenennen oder löschen.</p>
        </div>
        <div class="panel-toolbar">
          <input
            v-model.trim="tournamentSearch"
            type="text"
            class="title-input"
            placeholder="Turnier suchen"
          />
          <label class="toolbar-checkbox">
            <input v-model="showOlderTournaments" type="checkbox" />
            Vergangene anzeigen
          </label>
          <span class="toolbar-meta">{{ filteredTournamentList.length }} / {{ list.length || 0 }}</span>
        </div>
        <ul v-if="filteredTournamentList.length > 0" class="event-list">
          <li
            v-for="t in filteredTournamentList"
            :key="t.id"
            class="event-item"
            :class="{
              selected: String(uploadedId) === String(t.id),
              'is-past': tournamentListStatus(t).className === 'is-past'
            }">
            <template v-if="editingTournamentId === t.id">
              <input
                ref="titleEditInput"
                v-model="editingTitle"
                class="title-input"
                @blur="saveTournamentTitle(t)"
                @keydown.enter="saveTournamentTitle(t)"
                @keydown.esc="cancelTitleEdit"
              />
            </template>
            <a
              v-else
              href="#"
              class="event-title"
              @click.prevent="uploadedId = String(t.id); reload();"
              >
                {{ t.title || ($t('officialTournaments.tournament') + ' #' + t.id) }}
              </a>
            <span v-if="editingTournamentId !== t.id && (t.termin || t.eventDate)" class="event-date"> — {{ t.termin || t.eventDate }}</span>
            <span v-if="editingTournamentId !== t.id" :class="['list-status-badge', tournamentListStatus(t).className]">
              {{ tournamentListStatus(t).label }}
            </span>
            <button
              v-if="editingTournamentId !== t.id"
              class="btn-icon"
              :title="$t('officialTournaments.editTitle')"
              @click.prevent="startTitleEdit(t)"
            >
              ✏️
            </button>
            <button class="btn-secondary" @click.prevent="removeTournament(t)" :title="$t('officialTournaments.delete')">🗑️</button>
          </li>
        </ul>
        <div v-else class="empty-state compact">
          <p>{{ tournamentSearch ? 'Keine passenden Turniere gefunden.' : $t('officialTournaments.noEvents') }}</p>
        </div>
      </div>
    </div>

    <div class="admin-panel history-panel">
      <div class="history-toggle">
        <div class="panel-header compact">
          <h3>{{ $t('officialTournaments.participations') }}</h3>
          <p>{{ historySummaryText }}</p>
        </div>
        <button class="btn-secondary" @click="showHistory = !showHistory">
          {{ showHistory ? 'Ausblenden' : 'Einblenden' }}
        </button>
      </div>
      <div v-if="showHistory" class="filters">
        <label for="participationRange">{{ $t('officialTournaments.timeRange') }}</label>
        <select id="participationRange" v-model="participationRange" @change="onParticipationRangeChange">
          <option value="3m">{{ $t('officialTournaments.last3Months') }}</option>
          <option value="6m">{{ $t('officialTournaments.last6Months') }}</option>
          <option value="12m">{{ $t('officialTournaments.last12Months') }}</option>
          <option value="2y">{{ $t('officialTournaments.last2Years') }}</option>
          <option value="prev">{{ $t('officialTournaments.previousSeason') }}</option>
          <option value="all">{{ $t('officialTournaments.all') }}</option>
        </select>
      </div>
      <table v-if="showHistory">
        <thead>
          <tr>
            <th>{{ $t('officialTournaments.member') }}</th>
            <th>{{ $t('officialTournaments.tournament') }}</th>
            <th>{{ $t('officialTournaments.competition') }}</th>
            <th>{{ $t('officialTournaments.date') }}</th>
            <th>{{ $t('officialTournaments.placement') }}</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="row in clubParticipationRows()" :key="row.key">
            <td>{{ row.memberName }}</td>
            <td>{{ row.tournamentName }}</td>
            <td>{{ row.competitionName }}</td>
            <td>{{ row.date }}</td>
            <td>{{ row.placement || '–' }}</td>
          </tr>
        </tbody>
      </table>
    </div>

    <div v-if="parsed && parsed.parsedData && parsed.parsedData.competitions && parsed.parsedData.competitions.length" class="workspace-detail">
      <div class="selected-tournament-strip">
        <div class="selected-tournament-main">
          <span class="selected-tournament-label">Aktives Turnier</span>
          <strong>{{ parsed.parsedData.title || '–' }}</strong>
        </div>
        <div class="selected-tournament-meta">
          <span>{{ parsed.parsedData.termin || '–' }}</span>
          <span v-if="tournamentLocationLabel">{{ tournamentLocationLabel }}</span>
          <span>{{ registrationDeadlineInfo.label }}</span>
        </div>
      </div>
      <div class="overview-header">
        <div class="overview-main">
          <div class="workflow-badges">
            <span
              v-for="notice in workflowNotices"
              :key="notice.label"
              :class="['workflow-badge', notice.statusClass]">
              {{ notice.label }}
            </span>
          </div>
          <div class="overview-stats">
            <div class="stat-card">
              <span class="stat-label">Offene Meldungen</span>
              <strong class="stat-value">{{ pendingAutoRegistrationCount }}</strong>
            </div>
            <div class="stat-card">
              <span class="stat-label">Angemeldet</span>
              <strong class="stat-value">{{ registrationSummary.registered }}</strong>
            </div>
            <div class="stat-card">
              <span class="stat-label">Teilgenommen</span>
              <strong class="stat-value">{{ registrationSummary.participated }}</strong>
            </div>
            <div class="stat-card">
              <span class="stat-label">Mitglieder</span>
              <strong class="stat-value">{{ participantsGroups.length }}</strong>
            </div>
          </div>
        </div>
        <div class="top-actions">
          <button
            v-if="primaryHeaderAction === 'auto-register'"
            class="btn-primary"
            :disabled="!canAutoRegister || autoRegistering"
            @click="autoRegisterParticipants">
            {{ autoRegistering ? 'Anmeldung läuft...' : 'Automatisch anmelden' }}
          </button>
          <button
            v-else-if="primaryHeaderAction === 'results'"
            class="btn-primary"
            @click="activeTab='results'">
            Ergebnisse pflegen
          </button>
          <button
            v-else
            class="btn-primary"
            @click="openMemberDialog"
            :disabled="!parsed || !activeMembers.length">
            {{ $t('officialTournaments.selectMembers') }}
          </button>
          <button
            v-if="primaryHeaderAction !== 'members'"
            class="btn-secondary"
            @click="openMemberDialog"
            :disabled="!parsed || !activeMembers.length">
            {{ $t('officialTournaments.selectMembers') }}
          </button>
          <button
            class="btn-secondary"
            :disabled="!selectedMemberIds.length"
            @click="generateMembersPdf">
            {{ $t('officialTournaments.pdfForSelectedMembers') }}
          </button>
          <button
            v-if="primaryHeaderAction !== 'auto-register'"
            class="btn-secondary"
            :disabled="!canAutoRegister || autoRegistering"
            @click="autoRegisterParticipants">
            {{ autoRegistering ? 'Anmeldung läuft...' : 'Automatisch anmelden' }}
          </button>
          <button
            v-if="primaryHeaderAction !== 'results'"
            class="btn-secondary"
            :disabled="!registrationSummary.registered"
            @click="activeTab='results'">
            Ergebnisse
          </button>
        </div>
      </div>
      <div class="tabs">
        <button :class="['tab', activeTab==='overview' ? 'active' : '']" @click="activeTab='overview'" :title="$t('officialTournaments.showParticipants')">Übersicht</button>
        <button :class="['tab', activeTab==='competitions' ? 'active' : '']" @click="activeTab='competitions'" :title="$t('officialTournaments.showCompetitions')">{{ $t('officialTournaments.competitions') }}</button>
        <button :class="['tab', activeTab==='results' ? 'active' : '']" @click="activeTab='results'" :title="$t('officialTournaments.showResults')">{{ $t('officialTournaments.results') }}</button>
      </div>
      <div v-if="activeTab==='overview'">
        <h3>Teilnehmer</h3>
        <div class="filters">
          <label for="participantsFilter">Status:</label>
          <select id="participantsFilter" v-model="participantsFilter">
            <option value="all">Ohne Filter</option>
            <option value="open">Offen / noch nicht angemeldet</option>
            <option value="registered">Angemeldet</option>
            <option value="participated">Hat gespielt</option>
          </select>
          <div style="flex:1;"></div>
          <button
            v-if="participantsFilter === 'open'"
            class="btn-secondary"
            :disabled="!participantsRows.some(row => row.wants)"
            @click="batchUpdateParticipants('register')">
            Alle Interessierten als angemeldet markieren
          </button>
          <button
            v-if="participantsFilter === 'registered'"
            class="btn-secondary"
            :disabled="!participantsRows.length"
            @click="batchUpdateParticipants('participate')">
            Alle als teilgenommen markieren
          </button>
          <button
            v-if="participantsFilter === 'participated'"
            class="btn-secondary"
            :disabled="!participantsRows.length"
            @click="batchUpdateParticipants('reset')">
            Alle zurücksetzen
          </button>
          <button class="btn-primary" @click="generateParticipantsPdf">{{ $t('officialTournaments.participantsPdf') }}</button>
        </div>
        <table>
          <thead>
            <tr>
              <th>Mitglied</th>
              <th>Konkurrenz</th>
              <th>Startzeit</th>
              <th>Status</th>
              <th>Aktion</th>
              <th>Platzierung</th>
            </tr>
          </thead>
          <tbody>
            <template v-for="group in participantsGroups" :key="group.memberId">
              <template v-for="(item, idx) in group.items" :key="item.key">
                <tr>
                  <td v-if="idx === 0" :rowspan="group.items.length" class="member-cell">{{ group.memberName }}</td>
                  <td class="indented">{{ item.competitionName }}</td>
                  <td>{{ item.start }}</td>
                  <td class="status-cell">
                    <span v-if="item.participated" class="status-badge status-played">Hat gespielt</span>
                    <span v-else-if="item.registered" class="status-badge status-registered">Angemeldet</span>
                    <span v-else-if="item.wants" class="status-badge status-wants">Möchte teilnehmen</span>
                    <span v-else class="status-badge status-none">Nicht interessiert</span>
                  </td>
                  <td class="action-cell">
                    <button
                      v-if="!item.participated && !item.registered && !item.wants"
                      @click="markMemberInterested(group.memberId)"
                      class="btn-status btn-register"
                      title="Als interessiert markieren">
                      Interessiert
                    </button>
                    <button
                      v-else-if="!item.participated && !item.registered && item.wants"
                      @click="updateStatus(item, 'register')"
                      class="btn-status btn-register"
                      title="Als angemeldet markieren">
                      Angemeldet
                    </button>
                    <button
                      v-else-if="item.registered && !item.participated"
                      @click="updateStatus(item, 'participate')"
                      class="btn-status btn-participate"
                      title="Als teilgenommen markieren">
                      Teilgenommen
                    </button>
                    <button
                      v-else-if="item.participated"
                      @click="updateStatus(item, 'reset')"
                      class="btn-status btn-reset"
                      title="Status zurücksetzen">
                      Zurücksetzen
                    </button>
                    <span v-else class="no-action">Keine Aktion</span>
                  </td>
                  <td>
                    <input
                      v-if="item.participated"
                      type="text"
                      :value="item.placement || ''"
                      @change="updatePlacement(item, $event.target.value)"
                      placeholder="z.B. 3. Platz"
                      class="placement-input"
                    />
                    <span v-else>{{ item.placement || '–' }}</span>
                  </td>
                </tr>
              </template>
            </template>
            <tr v-if="!participantsGroups.length">
              <td colspan="6"><em>Keine Einträge für den gewählten Filter.</em></td>
            </tr>
          </tbody>
        </table>
      </div>
      <div v-else-if="activeTab==='competitions'">
      <h3>{{ $t('officialTournaments.competitions') }}</h3>
      <table>
        <thead>
          <tr>
            <th></th>
            <th>{{ $t('officialTournaments.ageClassCompetition') }}</th>
            <th>{{ $t('officialTournaments.startTime') }}</th>  
            <th>Status</th>
            <th>{{ $t('officialTournaments.entryFee') }}</th>
          </tr>
        </thead>
        <tbody>
          <template v-for="(c,idx) in parsed.parsedData.competitions" :key="idx">
            <tr>
              <td style="width:2.5rem;">
                <button class="btn-secondary" @click.prevent="toggleRow(c, idx)" :aria-expanded="isExpanded(c, idx)" title="Details ein-/ausklappen">
                  {{ isExpanded(c, idx) ? '▾' : '▸' }}
                </button>
              </td>
              <td>{{ c.ageClassCompetition || c.altersklasseWettbewerb }}</td>
              <td>{{ c.startTime || c.startzeit || '–' }}</td>
              <td>
                <div class="competition-summary">
                  <span class="competition-summary-chip">{{ eligibleMembers(c).length }} spielberechtigt</span>
                  <span class="competition-summary-chip">{{ competitionStatusSummary(c).interested }} interessiert</span>
                  <span class="competition-summary-chip">{{ competitionStatusSummary(c).registered }} angemeldet</span>
                  <span class="competition-summary-chip">{{ competitionStatusSummary(c).participated }} gespielt</span>
                </div>
              </td>
              <td>{{ c.entryFee || c.startgeld || '–' }}</td>
            </tr>
            <tr v-if="isExpanded(c, idx)" class="comp-details">
              <td :colspan="5">
                <div class="details">
                  <div class="competition-detail-grid">
                    <div class="detail-item"><strong>{{ $t('officialTournaments.deadlineDate') }}</strong> {{ c.registrationDeadlineDate || c.meldeschlussDatum || '–' }}</div>
                    <div class="detail-item"><strong>{{ $t('officialTournaments.deadlineOnline') }}</strong> {{ c.registrationDeadlineOnline || c.meldeschlussOnline || '–' }}</div>
                    <div class="detail-item"><strong>{{ $t('officialTournaments.cutoffDate') }}</strong> {{ c.cutoffDate || c.stichtag || '–' }}</div>
                    <div class="detail-item"><strong>{{ $t('officialTournaments.openTo') }}</strong> {{ c.openTo || c.offenFuer || '–' }}</div>
                    <div class="detail-item"><strong>{{ $t('officialTournaments.preliminaryRound') }}</strong> {{ c.preliminaryRound || c.vorrunde || '–' }}</div>
                    <div class="detail-item"><strong>{{ $t('officialTournaments.finalRound') }}</strong> {{ c.finalRound || c.endrunde || '–' }}</div>
                  </div>
                  <div class="competition-active-block">
                    <strong>Aktive Meldungen</strong>
                    <div v-if="activeCompetitionMembers(c).length" class="competition-active-list">
                      <div v-for="row in activeCompetitionMembers(c)" :key="row.key" class="competition-active-row">
                        <div class="competition-active-main">
                          <span class="competition-active-name">{{ row.memberName }}</span>
                          <span :class="['status-badge', row.statusClass]">{{ row.statusText }}</span>
                        </div>
                        <div class="competition-active-actions">
                          <button
                            v-if="row.wants && !row.registered && !row.participated"
                            @click="updateStatus(row, 'register')"
                            class="btn-status btn-register">
                            Angemeldet
                          </button>
                          <button
                            v-else-if="row.registered && !row.participated"
                            @click="updateStatus(row, 'participate')"
                            class="btn-status btn-participate">
                            Teilgenommen
                          </button>
                          <button
                            v-else-if="row.participated"
                            @click="updateStatus(row, 'reset')"
                            class="btn-status btn-reset">
                            Zurücksetzen
                          </button>
                          <input
                            v-if="row.participated"
                            type="text"
                            :value="row.placement || ''"
                            @change="updatePlacement(row, $event.target.value)"
                            placeholder="z.B. 3. Platz"
                            class="placement-input"
                          />
                        </div>
                      </div>
                    </div>
                    <p v-else class="competition-empty-note">Noch keine aktiven Meldungen in dieser Konkurrenz.</p>
                  </div>
                </div>
              </td>
            </tr>
          </template>
        </tbody>
      </table>
      </div>
      <div v-else>
        <h3>{{ $t('officialTournaments.results') }}</h3>
        <table>
          <thead>
            <tr>
              <th>Mitglied</th>
              <th>Konkurrenz</th>
              <th>Startzeit</th>
              <th>Status</th>
              <th>Aktion</th>
              <th>Platzierung</th>
            </tr>
          </thead>
          <tbody>
            <template v-for="group in resultsGroups" :key="group.memberId">
              <template v-for="(row, idx) in group.items" :key="row.key">
                <tr>
                  <td v-if="idx === 0" :rowspan="group.items.length" class="member-cell">{{ group.memberName }}</td>
                  <td class="indented">{{ row.competitionName }}</td>
                  <td>{{ row.start }}</td>
                  <td class="status-cell">
                    <span :class="['status-badge', row.participated ? 'status-played' : 'status-registered']">
                      {{ row.participated ? 'Hat gespielt' : 'Angemeldet' }}
                    </span>
                  </td>
                  <td class="action-cell">
                    <button
                      @click="updateStatus(row, 'reset')"
                      class="btn-status btn-reset"
                      title="Status zurücksetzen">
                      Zurücksetzen
                    </button>
                  </td>
                  <td>
                    <input
                      v-if="row.participated"
                      type="text"
                      :value="row.placement || ''"
                      @change="updatePlacement(row, $event.target.value)"
                      placeholder="z.B. 3. Platz"
                      class="placement-input"
                    />
                    <span v-else>{{ row.placement || '–' }}</span>
                  </td>
                </tr>
              </template>
            </template>
            <tr v-if="!resultsGroups.length">
              <td colspan="6"><em>Keine Ergebnisse vorhanden.</em></td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    <div v-else class="workspace-detail empty-workspace">
      <div class="empty-workspace-card">
        <h3>Kein aktives Turnier ausgewählt</h3>
        <p>Wähle links ein gespeichertes Turnier aus oder importiere zuerst eine Ausschreibung als PDF.</p>
      </div>
    </div>

    <!-- Member Selection Dialog -->
    <MemberSelectionDialog
      v-model="showMemberDialog"
      :members="activeMembers"
      v-model:selected-ids="selectedMemberIds"
      v-model:active-member-id="selectedMemberIdForDialog"
      :recommendations="selectedMemberCompetitions"
      :recommended-keys="getRecommendedKeys()"
      @select-all="selectAllMembers"
      @deselect-all="deselectAllMembers"
      @toggle-member="handleMemberToggle"
      @toggle-recommendation="handleRecommendationToggle"
      @generate-pdf="generateMembersPdfAndClose"
      @close="closeMemberDialog"
    />
    
    <!-- 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)"
    />
  </div>
</template>

<script>
import { mapGetters } from 'vuex';
import apiClient from '../apiClient.js';
import { getSafeErrorMessage, getSafeMessage } from '../utils/errorMessages.js';
import InfoDialog from '../components/InfoDialog.vue';
import ConfirmDialog from '../components/ConfirmDialog.vue';
import { buildInfoConfig, buildConfirmConfig, safeErrorMessage } from '../utils/dialogUtils.js';
import PDFGenerator from '../components/PDFGenerator.js';

import BaseDialog from '../components/BaseDialog.vue';
import MemberSelectionDialog from '../components/MemberSelectionDialog.vue';
export default {
  name: 'OfficialTournaments',
  components: {
    InfoDialog,
    ConfirmDialog,
    BaseDialog,
    MemberSelectionDialog
  },
  data() {
    return {
            // Dialog States
            infoDialog: {
                isOpen: false,
                title: '',
                message: '',
                details: '',
                type: 'info'
            },
            confirmDialog: {
                isOpen: false,
                title: '',
                message: '',
                details: '',
                type: 'info',
                resolveCallback: null
            },
      selectedFile: null,
      uploadedId: null,
      parsed: null,
      members: [],
      matches: {},
      list: [],
      expanded: {},
      selectedMemberIds: [],
      showMemberDialog: false,
      memberRecommendations: {},
      selectedMemberIdForDialog: null,
      participationMap: {}, // key: `${competitionId}-${memberId}` => { wants, registered, participated, placement }
      collator: new Intl.Collator('de', { sensitivity: 'base' }),
      activeTab: 'overview',
      participantsFilter: 'all',
      showHistory: false,
      loadingClubParticipations: false,
      clubParticipationRowsData: [],
      participationRange: 'all',
      tournamentSearch: '',
      showOlderTournaments: false,
      editingTournamentId: null,
      editingTitle: '',
      autoRegistering: false,
    };
  },
  computed: {
    ...mapGetters(['currentClub']),
    activeMembers() {
      return (this.members || [])
        .filter(m => m.active === true)
        .slice()
        .sort((a, b) => this.compareMembers(a, b));
    },
    selectedMemberInDialog() {
      const id = this.selectedMemberIdForDialog;
      if (!id) return null;
      return (this.members || []).find(m => m.id === id) || null;
    },
    selectedMemberCompetitions() {
      const m = this.selectedMemberInDialog;
      if (!m) return [];
      return this.competitionsForMember(m);
    },
    filteredTournamentList() {
      const search = String(this.tournamentSearch || '').trim().toLowerCase();
      const searchDate = this.parseDateFlexible(search);
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      const cutoff = new Date(today);
      cutoff.setDate(cutoff.getDate() - 14);

      const base = (Array.isArray(this.list) ? this.list.slice() : [])
        .filter((tournament) => {
          const isSelected = String(this.uploadedId) === String(tournament.id);
          const range = this.tournamentDateRange(tournament);
          const end = range.end || range.start;
          if (!this.showOlderTournaments && end && end.getTime() < cutoff.getTime() && !isSelected) {
            return false;
          }
          if (!search) return true;
          if (searchDate) {
            if (!range.start) return false;
            const endDate = range.end || range.start;
            return searchDate.getTime() >= range.start.getTime() && searchDate.getTime() <= endDate.getTime();
          }
          const haystack = [
            tournament.title,
            tournament.termin,
            tournament.eventDate,
          ].filter(Boolean).join(' ').toLowerCase();
          return haystack.includes(search);
        });

      return base.sort((a, b) => {
        const rangeA = this.tournamentDateRange(a);
        const rangeB = this.tournamentDateRange(b);
        const timeA = (rangeA.start || rangeA.end)?.getTime?.() ?? 0;
        const timeB = (rangeB.start || rangeB.end)?.getTime?.() ?? 0;
        return timeB - timeA;
      });
    },
    historySummaryText() {
      const rows = this.clubParticipationRows();
      if (this.loadingClubParticipations) return 'Turnierbeteiligungen werden geladen';
      if (!rows.length) return 'Keine Turnierbeteiligungen im gewählten Zeitraum';
      const uniqueMembers = new Set(rows.map((row) => row.memberName)).size;
      return `${rows.length} Einträge von ${uniqueMembers} Mitgliedern im Zeitraum ${this.participationRange}`;
    },
    resultsRows() {
      const comps = (this.parsed?.parsedData?.competitions) || [];
      const compById = Object.fromEntries(comps.map(c => [String(c.id), c]));
      const rows = [];
      const entries = this.participationMap || {};
      for (const [key, p] of Object.entries(entries)) {
        if (!p || !p.participated) continue;
        const [competitionId, memberId] = key.split('-');
        const c = compById[String(competitionId)];
        if (!c) continue;
        const mname = this.memberNameById(memberId);
        const start = String(c.startTime || c.startzeit || '–');
        rows.push({
          key: `${competitionId}-${memberId}`,
          memberName: mname,
          competitionName: c.ageClassCompetition || c.altersklasseWettbewerb || '',
          start,
          registered: !!p.registered,
          participated: !!p.participated,
          placement: p.placement || null,
        });
      }
      return rows.sort((a, b) => {
        const m = this.collator.compare(a.memberName, b.memberName);
        if (m !== 0) return m;
        return this.collator.compare(a.competitionName, b.competitionName);
      });
    },
    resultsGroups() {
      const groups = [];
      const byMember = new Map();
      for (const row of this.resultsRows) {
        const key = row.memberName;
        if (!byMember.has(key)) byMember.set(key, []);
        byMember.get(key).push(row);
      }
      for (const [memberName, items] of byMember.entries()) {
        items.sort((a, b) => this.collator.compare(a.competitionName, b.competitionName));
        groups.push({
          memberName,
          memberId: items[0]?.key.split('-')[1] || memberName,
          items,
        });
      }
      groups.sort((a, b) => this.collator.compare(a.memberName, b.memberName));
      return groups;
    },
    participantsRows() {
      const comps = (this.parsed?.parsedData?.competitions) || [];
      const compById = Object.fromEntries(comps.map(c => [String(c.id), c]));
      const rows = [];
      // Merge Quelle: parsed.participation + aktueller UI-Status aus participationMap
      const seen = new Set();
      const merged = [];
      if (Array.isArray(this.parsed?.participation)) {
        for (const e of this.parsed.participation) {
          const competitionId = String(e.competitionId);
          const memberId = String(e.memberId);
          const key = `${competitionId}-${memberId}`;
          seen.add(key);
          merged.push({ competitionId, memberId });
        }
      }
      for (const [key, p] of Object.entries(this.participationMap || {})) {
        if (seen.has(key)) continue;
        const [competitionId, memberId] = key.split('-');
        merged.push({ competitionId: String(competitionId), memberId: String(memberId) });
      }
      for (const e of merged) {
        const competitionId = String(e.competitionId);
        const memberId = String(e.memberId);
        const c = compById[competitionId];
        if (!c) continue;
        // Hole aktuellen Status (inkl. UI-Änderungen) aus participationMap
        const current = this.getParticipation(competitionId, memberId);
        const mname = this.memberNameById(memberId);
        const start = String(c.startTime || c.startzeit || '–');
        const base = {
          key: `${competitionId}-${memberId}`,
          memberName: mname,
          competitionName: c.ageClassCompetition || c.altersklasseWettbewerb || '',
          start,
          registered: !!current.registered,
          participated: !!current.participated,
          placement: current.placement || null,
          wants: !!current.wants,
        };
        if (this.participantsFilter === 'all') {
          rows.push(base);
        } else if (this.participantsFilter === 'open') {
          if (!base.registered && !base.participated) rows.push(base);
        } else if (this.participantsFilter === 'registered') {
          if (base.registered && !base.participated) rows.push(base);
        } else if (this.participantsFilter === 'participated') {
          if (base.participated) rows.push(base);
        }
      }
      return rows.sort((a, b) => {
        const m = this.collator.compare(a.memberName, b.memberName);
        if (m !== 0) return m;
        return this.collator.compare(a.competitionName, b.competitionName);
      });
    },
    participantsGroups() {
      const groups = [];
      const byMember = new Map();
      for (const row of this.participantsRows) {
        const key = row.memberName;
        if (!byMember.has(key)) byMember.set(key, []);
        byMember.get(key).push(row);
      }
      for (const [memberName, items] of byMember.entries()) {
        items.sort((a, b) => this.collator.compare(a.competitionName, b.competitionName));
        groups.push({
          memberName,
          memberId: items[0]?.key.split('-')[1] || memberName,
          items,
        });
      }
      groups.sort((a, b) => this.collator.compare(a.memberName, b.memberName));
      return groups;
    },
    participantOverviewRows() {
      const allRows = [];
      const comps = (this.parsed?.parsedData?.competitions) || [];
      const compById = Object.fromEntries(comps.map(c => [String(c.id), c]));
      const seen = new Set();
      const merged = [];

      if (Array.isArray(this.parsed?.participation)) {
        for (const e of this.parsed.participation) {
          const key = `${e.competitionId}-${e.memberId}`;
          if (seen.has(key)) continue;
          seen.add(key);
          merged.push({ competitionId: String(e.competitionId), memberId: String(e.memberId) });
        }
      }

      for (const key of Object.keys(this.participationMap || {})) {
        if (seen.has(key)) continue;
        const [competitionId, memberId] = key.split('-');
        merged.push({ competitionId: String(competitionId), memberId: String(memberId) });
      }

      const byMember = new Map();
      for (const entry of merged) {
        const competitionId = String(entry.competitionId);
        const memberId = String(entry.memberId);
        const competition = compById[competitionId];
        if (!competition) continue;
        const participation = this.getParticipation(competitionId, memberId);
        if (!byMember.has(memberId)) {
          byMember.set(memberId, {
            memberId,
            memberName: this.memberNameById(memberId),
            competitionNames: [],
            wantsCount: 0,
            readyCount: 0,
            registeredCount: 0,
            participatedCount: 0,
          });
        }
        const row = byMember.get(memberId);
        const competitionName = competition.ageClassCompetition || competition.altersklasseWettbewerb || '';
        if (competitionName && !row.competitionNames.includes(competitionName)) {
          row.competitionNames.push(competitionName);
        }
        if (participation.wants) row.wantsCount += 1;
        if (participation.wants && !participation.registered && !participation.participated) row.readyCount += 1;
        if (participation.registered) row.registeredCount += 1;
        if (participation.participated) row.participatedCount += 1;
      }

      for (const selectedMemberId of this.selectedMemberIds || []) {
        const memberId = String(selectedMemberId);
        if (byMember.has(memberId)) continue;
        const member = (this.members || []).find((entry) => String(entry.id) === memberId);
        if (!member) continue;

        const eligibleCompetitions = comps.filter((competition) => this.isEligibleForCompetition(member, competition));
        if (!eligibleCompetitions.length) continue;

        byMember.set(memberId, {
          memberId,
          memberName: this.memberNameById(memberId),
          competitionNames: eligibleCompetitions.map((competition) => competition.ageClassCompetition || competition.altersklasseWettbewerb || '').filter(Boolean),
          wantsCount: 0,
          readyCount: 0,
          registeredCount: 0,
          participatedCount: 0,
        });
      }

      for (const row of byMember.values()) {
        row.competitionNames.sort((a, b) => this.collator.compare(a, b));
        row.canMarkInterested = row.competitionNames.length > 0
          && row.wantsCount === 0
          && row.registeredCount === 0
          && row.participatedCount === 0;
        if (row.participatedCount > 0) {
          row.statusText = 'Hat gespielt';
          row.statusClass = 'status-played';
        } else if (row.registeredCount > 0) {
          row.statusText = 'Angemeldet';
          row.statusClass = 'status-registered';
        } else if (row.readyCount > 0) {
          row.statusText = 'Möchte teilnehmen';
          row.statusClass = 'status-wants';
        } else {
          row.statusText = 'Nicht interessiert';
          row.statusClass = 'status-none';
        }
        allRows.push(row);
      }

      return allRows.sort((a, b) => this.collator.compare(a.memberName, b.memberName));
    },
    registrationSummary() {
      return {
        registered: Object.values(this.participationMap || {}).filter((entry) => entry?.registered && !entry?.participated).length,
        participated: Object.values(this.participationMap || {}).filter((entry) => entry?.participated).length
      };
    },
    tournamentLocationLabel() {
      const places = this.parsed?.parsedData?.austragungsorte || [];
      const placeLine = places.find((entry) => /^PLZ\s*\/\s*Ort:/i.test(entry || ''));
      return placeLine ? placeLine.replace(/^PLZ\s*\/\s*Ort:/i, '').trim() : '';
    },
    registrationDeadlineAt() {
      const deadlines = [
        ...(this.parsed?.parsedData?.meldeschluesse || []),
        ...(((this.parsed?.parsedData?.competitions) || []).flatMap((competition) => [
          competition.registrationDeadlineDate,
          competition.registrationDeadlineOnline,
          competition.meldeschlussDatum,
          competition.meldeschlussOnline,
        ])),
      ].filter(Boolean);

      let earliest = null;
      for (const entry of deadlines) {
        const parsed = this.parseDateTimeFlexible(entry);
        if (!parsed) continue;
        if (!earliest || parsed.getTime() < earliest.getTime()) {
          earliest = parsed;
        }
      }
      return earliest;
    },
    registrationDeadlineInfo() {
      let earliestText = '';
      const deadlines = [
        ...(this.parsed?.parsedData?.meldeschluesse || []),
        ...(((this.parsed?.parsedData?.competitions) || []).flatMap((competition) => [
          competition.registrationDeadlineDate,
          competition.registrationDeadlineOnline,
          competition.meldeschlussDatum,
          competition.meldeschlussOnline,
        ])),
      ].filter(Boolean);
      for (const entry of deadlines) {
        const parsed = this.parseDateTimeFlexible(entry);
        if (parsed && this.registrationDeadlineAt && parsed.getTime() === this.registrationDeadlineAt.getTime()) {
          earliestText = String(entry);
          break;
        }
      }

      if (!this.registrationDeadlineAt) {
        return { label: 'Meldeschluss unbekannt', statusClass: 'is-neutral' };
      }

      if (this.registrationDeadlineAt.getTime() < Date.now()) {
        return { label: `Meldeschluss abgelaufen: ${earliestText}`, statusClass: 'is-danger' };
      }

      return { label: `Meldeschluss offen: ${earliestText}`, statusClass: 'is-success' };
    },
    canAutoRegister() {
      if (!this.uploadedId || !this.pendingAutoRegistrationCount) return false;
      if (!this.registrationDeadlineAt) return true;
      return this.registrationDeadlineAt.getTime() >= Date.now();
    },
    workflowNotices() {
      const notices = [];
      if (this.pendingAutoRegistrationCount > 0) {
        notices.push({
          label: `${this.pendingAutoRegistrationCount} zur Anmeldung bereit`,
          statusClass: 'is-warning',
        });
      }
      if (this.registrationSummary.registered > 0) {
        notices.push({
          label: `${this.registrationSummary.registered} Ergebnisse offen`,
          statusClass: 'is-info',
        });
      }
      if (!this.pendingAutoRegistrationCount && !this.registrationSummary.registered) {
        notices.push({
          label: 'Keine offenen Aufgaben',
          statusClass: 'is-neutral',
        });
      }
      return notices;
    },
    primaryHeaderAction() {
      if (this.pendingAutoRegistrationCount > 0) return 'auto-register';
      if (this.registrationSummary.registered > 0) return 'results';
      return 'members';
    },
    pendingAutoRegistrationCount() {
      return Object.values(this.participationMap || {}).filter((entry) =>
        entry?.wants && !entry?.registered && !entry?.participated
      ).length;
    }
  },
  methods: {
        // Dialog Helper Methods
        async showInfo(title, message, details = '', type = 'info') {
            this.infoDialog = buildInfoConfig({ title, message, details, type });
        },
        
        async showConfirm(title, message, details = '', type = 'info', options = {}) {
            return new Promise((resolve) => {
                this.confirmDialog = buildConfirmConfig({
                    title,
                    message,
                    details,
                    type,
                    resolveCallback: resolve,
                    ...options,
                });
            });
        },
        
        handleConfirmResult(confirmed) {
            if (this.confirmDialog.resolveCallback) {
                this.confirmDialog.resolveCallback(confirmed);
                this.confirmDialog.resolveCallback = null;
            }
            this.confirmDialog.isOpen = false;
        },
        
    compareMembers(a, b) {
      const fnA = String(a.firstName || '');
      const fnB = String(b.firstName || '');
      const lnA = String(a.lastName || '');
      const lnB = String(b.lastName || '');
      const byFirst = this.collator.compare(fnA, fnB);
      if (byFirst !== 0) return byFirst;
      return this.collator.compare(lnA, lnB);
    },
    tournamentDateRange(tournament) {
      const raw = String(tournament?.termin || tournament?.eventDate || '').trim();
      if (!raw) return { start: null, end: null };
      const matches = raw.match(/(\d{1,2}\.\d{1,2}\.\d{4}|\d{4}-\d{2}-\d{2})/g) || [];
      const dates = matches
        .map((entry) => this.parseDateFlexible(entry))
        .filter(Boolean)
        .sort((a, b) => a.getTime() - b.getTime());
      if (!dates.length) return { start: null, end: null };
      return {
        start: dates[0],
        end: dates[dates.length - 1],
      };
    },
    tournamentListStatus(tournament) {
      if (String(this.uploadedId) === String(tournament.id)) {
        return { label: 'Aktiv', className: 'is-active' };
      }
      const range = this.tournamentDateRange(tournament);
      const date = range.end || range.start;
      if (!date) return { label: 'Ohne Datum', className: 'is-neutral' };
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      if (date.getTime() < today.getTime()) {
        return { label: 'Vergangen', className: 'is-past' };
      }
      return { label: 'Kommend', className: 'is-upcoming' };
    },
    competitionStatusSummary(c) {
      const summary = { interested: 0, registered: 0, participated: 0 };
      for (const member of this.eligibleMembers(c)) {
        const participation = this.getParticipation(c.id, member.id);
        if (participation.participated) {
          summary.participated += 1;
        } else if (participation.registered) {
          summary.registered += 1;
        } else if (participation.wants) {
          summary.interested += 1;
        }
      }
      return summary;
    },
    activeCompetitionMembers(c) {
      const rows = [];
      for (const member of this.eligibleMembers(c)) {
        const participation = this.getParticipation(c.id, member.id);
        if (!participation.wants && !participation.registered && !participation.participated) continue;
        const row = {
          key: `${c.id}-${member.id}`,
          memberName: this.memberNameById(member.id),
          competitionName: c.ageClassCompetition || c.altersklasseWettbewerb || '',
          start: String(c.startTime || c.startzeit || '–'),
          wants: !!participation.wants,
          registered: !!participation.registered,
          participated: !!participation.participated,
          placement: participation.placement || null,
          statusClass: 'status-none',
          statusText: 'Nicht interessiert',
        };
        if (row.participated) {
          row.statusClass = 'status-played';
          row.statusText = 'Hat gespielt';
        } else if (row.registered) {
          row.statusClass = 'status-registered';
          row.statusText = 'Angemeldet';
        } else if (row.wants) {
          row.statusClass = 'status-wants';
          row.statusText = 'Möchte teilnehmen';
        }
        rows.push(row);
      }
      return rows.sort((a, b) => this.collator.compare(a.memberName, b.memberName));
    },
    generateParticipantsPdf() {
      if (!this.parsed) return;
      const title = this.parsed?.parsedData?.title || 'Offizielles Turnier';
      const dateText = this.parsed?.parsedData?.termin || '';
      // Alle Teilnehmer unabhängig vom Filter
      const comps = (this.parsed?.parsedData?.competitions) || [];
      const compById = Object.fromEntries(comps.map(c => [String(c.id), c]));
      const allRows = [];
      const seen = new Set();
      const merged = [];
      if (Array.isArray(this.parsed?.participation)) {
        for (const e of this.parsed.participation) {
          const competitionId = String(e.competitionId);
          const memberId = String(e.memberId);
          const key = `${competitionId}-${memberId}`;
          seen.add(key);
          merged.push({ competitionId, memberId });
        }
      }
      for (const [key, p] of Object.entries(this.participationMap || {})) {
        if (seen.has(key)) continue;
        const [competitionId, memberId] = key.split('-');
        merged.push({ competitionId: String(competitionId), memberId: String(memberId) });
      }
      for (const e of merged) {
        const competitionId = String(e.competitionId);
        const memberId = String(e.memberId);
        const c = compById[competitionId];
        if (!c) continue;
        const current = this.getParticipation(competitionId, memberId);
        const mname = this.memberNameById(memberId);
        const start = String(c.startTime || c.startzeit || '–');
        let statusStyle = 'normal';
        let statusText = '';
        if (current.participated) {
          statusStyle = 'normal';
          statusText = 'gespielt';
        } else if (current.registered) {
          statusStyle = 'italic';
          statusText = 'angemeldet';
        } else if (current.wants) {
          statusStyle = 'bold';
          statusText = 'möchte teilnehmen (Anmeldung fehlt)';
        }
        allRows.push({
          memberName: mname,
          competitionName: c.ageClassCompetition || c.altersklasseWettbewerb || '',
          start,
          placement: current.placement || '',
          statusStyle,
          statusText,
          wants: current.wants,
          registered: current.registered,
          participated: current.participated,
        });
      }
      // Nach Mitglied und Konkurrenz sortieren
      allRows.sort((a, b) => {
        const m = this.collator.compare(a.memberName, b.memberName);
        if (m !== 0) return m;
        return this.collator.compare(a.competitionName, b.competitionName);
      });
      
      // Gruppierung nach Mitglied mit korrekter Formatierungslogik
      const groups = [];
      const byMember = new Map();
      for (const row of allRows) {
        const key = row.memberName;
        if (!byMember.has(key)) byMember.set(key, []);
        byMember.get(key).push(row);
      }
      
      for (const [memberName, items] of byMember.entries()) {
        items.sort((a, b) => this.collator.compare(a.competitionName, b.competitionName));
        
        // Bestimme Formatierung für den Spielernamen basierend auf allen Konkurrenzen
        let memberStyle = 'normal';
        let memberStatusText = '';
        
        const hasPlayed = items.some(item => item.participated);
        const wantsToPlay = items.some(item => item.wants);
        const allRegistered = items.every(item => !item.wants || item.registered);
        const hasUnregistered = items.some(item => item.wants && !item.registered);
        
        if (hasPlayed) {
          memberStyle = 'normal';
          memberStatusText = 'hat gespielt';
        } else if (wantsToPlay && hasUnregistered) {
          memberStyle = 'bold';
          memberStatusText = 'Anmeldung fehlt';
        } else if (wantsToPlay && allRegistered) {
          memberStyle = 'italic';
          memberStatusText = 'angemeldet';
        } else {
          // Fallback für Spieler ohne Teilnahmewünsche
          memberStyle = 'normal';
          memberStatusText = '';
        }
        
        groups.push({
          memberName,
          memberId: items[0]?.memberName || memberName,
          memberStyle,
          memberStatusText,
          items,
        });
      }
      groups.sort((a, b) => this.collator.compare(a.memberName, b.memberName));
      
      const pdf = new PDFGenerator(20, 10, this.$t);
      pdf.addParticipantsSummary(title, dateText, groups);
      pdf.save('teilnehmer.pdf');
    },
    clubParticipationRows() {
      if (this.clubParticipationRowsData && this.clubParticipationRowsData.length) {
        return this.clubParticipationRowsData;
      }
      // Fallback: nur aktuelles Turnier
      const rows = [];
      if (!this.parsed || !this.parsed.parsedData) return rows;
      const parts = (this.parsed.participation) || [];
      const comps = this.parsed.parsedData.competitions || [];
      const compById = Object.fromEntries(comps.map(c => [String(c.id), c]));
      for (const p of parts) {
        if (!p.participated) continue;
        const c = compById[String(p.competitionId)];
        if (!c) continue;
        const mname = this.memberNameById(p.memberId);
        const date = (String(c.startTime || c.startzeit || '')).match(/(\d{1,2}\.\d{1,2}\.\d{4})/);
        rows.push({
          key: `club-${p.competitionId}-${p.memberId}`,
          memberName: mname,
          competitionName: c.ageClassCompetition || c.altersklasseWettbewerb || '',
          date: date ? date[1] : '–',
          placement: p.placement || null,
        });
      }
      return rows.sort((a, b) => {
        const byMember = this.collator.compare(a.memberName, b.memberName);
        if (byMember !== 0) return byMember;
        return this.collator.compare(a.competitionName, b.competitionName);
      });
    },
    async ensureMembersLoaded() {
      if (!this.members || !this.members.length) {
        const m = await apiClient.get(`/clubmembers/get/${this.currentClub}/true`);
        this.members = m.data;
      }
    },
    computeRange() {
      const now = new Date();
      let from = null, to = null;
      switch (this.participationRange) {
        case '3m': from = new Date(now); from.setMonth(from.getMonth() - 3); break;
        case '6m': from = new Date(now); from.setMonth(from.getMonth() - 6); break;
        case '12m': from = new Date(now); from.setMonth(from.getMonth() - 12); break;
        case '2y': from = new Date(now); from.setFullYear(from.getFullYear() - 2); break;
        case 'prev': {
          const y = now.getMonth() + 1 >= 7 ? now.getFullYear() : now.getFullYear() - 1;
          from = new Date(y - 1, 6, 1); // 01.07.(y-1)
          to = new Date(y, 5, 30, 23, 59, 59, 999); // 30.06.y
          break;
        }
        case 'all':
        default:
          from = null; to = null; break;
      }
      return { from, to };
    },
    dateFromDmy(dmy) {
      if (!dmy) return null;
      const m = String(dmy).match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);
      if (!m) return null;
      const d = new Date(Number(m[3]), Number(m[2]) - 1, Number(m[1]));
      return isNaN(d.getTime()) ? null : d;
    },
    onParticipationRangeChange() {
      // Neu laden, damit serverseitig bereits gefiltert werden könnte; ansonsten clientseitig filtern
      this.loadClubParticipations();
    },
    async loadClubParticipations() {
      if (this.loadingClubParticipations) return;
      this.loadingClubParticipations = true;
      try {
        await this.ensureMembersLoaded();
        // neuen kompakten EP nutzen
        const r = await apiClient.get(`/official-tournaments/${this.currentClub}/participations/summary`);
        const rows = [];
        const { from, to } = this.computeRange();
        for (const t of (r.data || [])) {
          for (const e of (t.entries || [])) {
            const d = this.dateFromDmy(e.date || t.startDate || null);
            if (from && d && d < from) continue;
            if (to && d && d > to) continue;
            rows.push({
              key: `club-${t.tournamentId}-${e.competitionId}-${e.memberId}`,
              memberName: e.memberName,
              tournamentName: t.tournamentName || t.title || `Turnier #${t.tournamentId}`,
              competitionName: e.competitionName,
              date: e.date || t.startDate || '–',
              placement: e.placement || null,
            });
          }
        }
        rows.sort((a, b) => {
          const byMember = this.collator.compare(a.memberName, b.memberName);
          if (byMember !== 0) return byMember;
          return this.collator.compare(a.competitionName, b.competitionName);
        });
        this.clubParticipationRowsData = rows;
      } finally {
        this.loadingClubParticipations = false;
      }
    },
    memberNameById(id) {
      const m = (this.members || []).find(x => String(x.id) === String(id));
      return m ? `${m.firstName} ${m.lastName}` : `#${id}`;
    },
    async onFile(e) {
      const file = e.target.files && e.target.files[0] ? e.target.files[0] : null;
      if (!file) {
        this.selectedFile = null;
        return;
      }
      const maxSize = 10 * 1024 * 1024; // 10 MB
      const extension = file.name.includes('.') ? file.name.split('.').pop().toLowerCase() : '';
      const isPdf = extension === 'pdf';
      const allowedMime = ['application/pdf', 'application/octet-stream'];
      if (!isPdf || (file.type && !allowedMime.includes(file.type))) {
        this.selectedFile = null;
        e.target.value = '';
        await this.showInfo('Ungültige Datei', 'Bitte wählen Sie eine PDF-Datei aus.', '', 'warning');
        return;
      }
      if (file.size > maxSize) {
        this.selectedFile = null;
        e.target.value = '';
        await this.showInfo('Datei zu groß', 'Die PDF darf maximal 10 MB groß sein.', '', 'warning');
        return;
      }
      this.selectedFile = file;
    },
    isExpanded(c, idx) {
      return !!this.expanded[String(idx)];
    },
    toggleRow(c, idx) {
      const k = String(idx);
      this.$set ? this.$set(this.expanded, k, !this.expanded[k]) : (this.expanded[k] = !this.expanded[k]);
    },
    async upload() {
      if (!this.selectedFile) return;
      const fd = new FormData();
      fd.append('pdf', this.selectedFile);
      try {
        const response = await apiClient.post(`/official-tournaments/${this.currentClub}/upload`, fd, {
          headers: { 'Content-Type': 'multipart/form-data' }
        });
        this.uploadedId = response.data.id;
        await this.reload();
        await this.loadList();
        await this.showInfo('Erfolg', 'PDF erfolgreich hochgeladen.', '', 'success');
      } catch (error) {
        const message = safeErrorMessage(error, this.$t('officialTournaments.uploadError'));
        await this.showInfo('Fehler', message, '', 'error');
      }
    },
    async autoRegisterParticipants() {
      if (!this.canAutoRegister || this.autoRegistering) return;
      this.autoRegistering = true;
      try {
        const response = await apiClient.post(
          `/official-tournaments/${this.currentClub}/${this.uploadedId}/auto-register`
        );
        if (!response?.data?.success) {
          throw new Error(response?.data?.error || 'Die automatische click-TT-Anmeldung ist fehlgeschlagen.');
        }
        await this.reload();
        await this.showInfo(
          'Erfolg',
          response?.data?.message || 'Die gewünschten Teilnehmer wurden automatisch in click-TT angemeldet.',
          '',
          'success'
        );
      } catch (error) {
        await this.showInfo(
          'Fehler',
          getSafeMessage(error) || 'Die automatische click-TT-Anmeldung ist fehlgeschlagen.',
          getSafeErrorMessage(error),
          'error'
        );
      } finally {
        this.autoRegistering = false;
      }
    },
    async reload() {
      if (!this.uploadedId) return;
      const t = await apiClient.get(`/official-tournaments/${this.currentClub}/${this.uploadedId}`);
      this.parsed = t.data;
      this.buildParticipationMap(t.data && t.data.participation ? t.data.participation : []);
      // Mitglieder laden (alle aktiv)
      const m = await apiClient.get(`/clubmembers/get/${this.currentClub}/true`);
      this.members = m.data;
    },
    async loadList() {
      try {
        const r = await apiClient.get(`/official-tournaments/${this.currentClub}`);
        this.list = Array.isArray(r.data) ? r.data : [];
      } catch (error) {
        console.error('[loadList] Error:', error);
        this.list = [];
        // Fehler wird nicht angezeigt, damit die Seite trotzdem funktioniert
      }
    },
    buildParticipationMap(entries) {
      const map = {};
      for (const e of entries) {
        const key = `${e.competitionId}-${e.memberId}`;
        map[key] = {
          wants: !!e.wants,
          registered: !!e.registered,
          participated: !!e.participated,
          placement: e.placement || null,
        };
      }
      this.participationMap = map;
    },
    getParticipation(competitionId, memberId) {
      const key = `${competitionId}-${memberId}`;
      if (!this.participationMap[key]) {
        this.$set ? this.$set(this.participationMap, key, { wants: false, registered: false, participated: false, placement: null })
                  : (this.participationMap[key] = { wants: false, registered: false, participated: false, placement: null });
      }
      return this.participationMap[key];
    },
    async saveParticipation(competitionId, memberId) {
      const key = `${competitionId}-${memberId}`;
      const p = this.participationMap[key] || { wants: false, registered: false, participated: false, placement: null };
      try {
        await apiClient.post(`/official-tournaments/${this.currentClub}/${this.uploadedId}/participation`, {
          competitionId,
          memberId,
          wants: !!p.wants,
          registered: !!p.registered,
          participated: !!p.participated,
          placement: p.placement || null,
        });
      } catch (e) {
        // optional: Fehleranzeige
      }
    },
    onToggleWants(c, m, val) {
      const key = `${c.id}-${m.id}`;
      this.getParticipation(c.id, m.id).wants = !!val;
      this.saveParticipation(c.id, m.id);
    },
    onToggleRegistered(c, m, val) {
      this.getParticipation(c.id, m.id).registered = !!val;
      this.saveParticipation(c.id, m.id);
    },
    onToggleParticipated(c, m, val) {
      this.getParticipation(c.id, m.id).participated = !!val;
      this.saveParticipation(c.id, m.id);
    },
    onPlacementChange(c, m, val) {
      const v = (val || '').trim();
      this.getParticipation(c.id, m.id).placement = v || null;
      this.saveParticipation(c.id, m.id);
    },
    async updateStatus(item, action) {
      try {
        const [competitionId, memberId] = item.key.split('-');
        const response = await apiClient.post(`/official-tournaments/${this.currentClub}/${this.uploadedId}/status`, {
          competitionId: parseInt(competitionId),
          memberId: parseInt(memberId),
          action: action
        });
        
        if (response.data.success) {
          // Aktualisiere den lokalen Status
          const key = `${competitionId}-${memberId}`;
          const participation = this.getParticipation(competitionId, memberId);
          participation.wants = response.data.status.wants;
          participation.registered = response.data.status.registered;
          participation.participated = response.data.status.participated;
          participation.placement = response.data.status.placement;
          
          // Aktualisiere auch die participationMap
          this.participationMap[key] = participation;
        }
      } catch (error) {
        console.error('Fehler beim Aktualisieren des Status:', error);
        this.showInfo(this.$t('common.error'), this.$t('officialTournaments.updateStatusError'), getSafeErrorMessage(error), 'error');
        throw error;
      }
    },
    async updatePlacement(item, value) {
      try {
        const [competitionId, memberId] = item.key.split('-');
        const participation = this.getParticipation(competitionId, memberId);
        participation.placement = value.trim() || null;
        await this.saveParticipation(competitionId, memberId);
      } catch (error) {
        console.error('Fehler beim Aktualisieren der Platzierung:', error);
        this.showInfo(this.$t('common.error'), this.$t('officialTournaments.updatePlacementError'), getSafeErrorMessage(error), 'error');
      }
    },
    async batchUpdateParticipants(action) {
      const rows = action === 'register'
        ? this.participantsRows.filter((row) => row.wants && !row.registered && !row.participated)
        : [...this.participantsRows];
      if (!rows.length) return;

      const actionLabel = action === 'register'
        ? 'als angemeldet markieren'
        : action === 'participate'
          ? 'als teilgenommen markieren'
          : 'zurücksetzen';

      const confirmed = await this.showConfirm(
        'Sammelaktion',
        `${rows.length} Einträge wirklich ${actionLabel}?`,
        '',
        action === 'reset' ? 'warning' : 'info'
      );
      if (!confirmed) return;

      let processed = 0;
      const failures = [];

      for (const row of rows) {
        try {
          await this.updateStatus(row, action);
          processed += 1;
        } catch (error) {
          failures.push(`${row.memberName} / ${row.competitionName}`);
        }
      }

      if (failures.length) {
        await this.showInfo(
          'Teilweise erfolgreich',
          `${processed} von ${rows.length} Einträgen wurden aktualisiert.`,
          failures.join('\n'),
          'warning'
        );
        return;
      }

      await this.showInfo(
        'Erfolg',
        `${processed} Einträge wurden ${actionLabel}.`,
        '',
        'success'
      );
    },
    async markMemberInterested(memberId) {
      const competitions = ((this.parsed?.parsedData?.competitions) || [])
        .filter((competition) => {
          const member = (this.members || []).find((entry) => String(entry.id) === String(memberId));
          return member && this.isEligibleForCompetition(member, competition);
        });

      for (const competition of competitions) {
        const participation = this.getParticipation(competition.id, memberId);
        if (participation.wants || participation.registered || participation.participated) continue;
        participation.wants = true;
        await this.saveParticipation(competition.id, memberId);
      }
    },
    async updateStatusForCompetition(competition, member, action) {
      try {
        const response = await apiClient.post(`/official-tournaments/${this.currentClub}/${this.uploadedId}/status`, {
          competitionId: competition.id,
          memberId: member.id,
          action: action
        });
        
        if (response.data.success) {
          // Aktualisiere den lokalen Status
          const participation = this.getParticipation(competition.id, member.id);
          participation.wants = response.data.status.wants;
          participation.registered = response.data.status.registered;
          participation.participated = response.data.status.participated;
          participation.placement = response.data.status.placement;
          
          // Aktualisiere auch die participationMap
          const key = `${competition.id}-${member.id}`;
          this.participationMap[key] = participation;
        }
      } catch (error) {
        console.error('Fehler beim Aktualisieren des Status:', error);
        this.showInfo(this.$t('common.error'), this.$t('officialTournaments.updateStatusError'), getSafeErrorMessage(error), 'error');
      }
    },
    // Auswahl Helfer + PDF-Generierung
    openMemberDialog() { this.showMemberDialog = true; },
    closeMemberDialog() { this.showMemberDialog = false; },
    toggleAllMembers() {
      const ids = this.activeMembers.map(m => m.id);
      if (this.selectedMemberIds.length === ids.length) this.selectedMemberIds = [];
      else this.selectedMemberIds = ids.slice();
    },
    selectAllMembers() { this.selectedMemberIds = this.activeMembers.map(m => m.id); },
    deselectAllMembers() { this.selectedMemberIds = []; },
    handleMemberToggle({ memberId, checked }) {
      this.selectedMemberIdForDialog = memberId;
      if (checked && !this.selectedMemberIds.includes(memberId)) {
        this.selectedMemberIds.push(memberId);
      } else if (!checked) {
        this.selectedMemberIds = this.selectedMemberIds.filter(id => id !== memberId);
      }
    },
    handleRecommendationToggle({ memberId, key, checked }) {
      if (checked) {
        if (!this.memberRecommendations[memberId]) {
          this.$set ? this.$set(this.memberRecommendations, memberId, new Set()) : (this.memberRecommendations[memberId] = new Set());
        }
        this.memberRecommendations[memberId].add(key);
      } else {
        if (this.memberRecommendations[memberId]) {
          this.memberRecommendations[memberId].delete(key);
        }
      }
    },
    getRecommendedKeys() {
      if (!this.selectedMemberIdForDialog || !this.memberRecommendations[this.selectedMemberIdForDialog]) {
        return [];
      }
      return Array.from(this.memberRecommendations[this.selectedMemberIdForDialog]);
    },
    splitDateTime(str) {
      if (!str) return { date: '–', time: '–' };
      const s = String(str);
      const dm = s.match(/(\d{1,2}\.\d{1,2}\.\d{4})/);
      const tm = s.match(/(\d{1,2}:\d{2})/);
      return { date: dm ? dm[1] : '–', time: tm ? tm[1] : '–' };
    },
    competitionsForMember(member) {
      const comps = (this.parsed && this.parsed.parsedData && this.parsed.parsedData.competitions) ? this.parsed.parsedData.competitions : [];
      const entryFees = (this.parsed && this.parsed.parsedData && this.parsed.parsedData.entryFees) ? this.parsed.parsedData.entryFees : {};
      const rows = [];
      for (let idx = 0; idx < comps.length; idx++) {
        const c = comps[idx];
        if (this.isEligibleForCompetition(member, c)) {
          const title = c.ageClassCompetition || c.altersklasseWettbewerb || '';
          const st = this.splitDateTime(c.startTime || c.startzeit || '');
          
          // Bestimme die Teilnahmegebühr für diese Altersklasse
          let entryFee = '';
          const ageClassMatch = title.match(/(U\d+|AK\s*\d+)/i);
          if (ageClassMatch) {
            const ageClass = ageClassMatch[1].toUpperCase().replace(/\s+/g, '');
            if (entryFees[ageClass]) {
              entryFee = `${entryFees[ageClass].amount}${entryFees[ageClass].currency}`;
            }
          }
          
          // Fallback: Verwende startgeld aus der Konkurrenz
          if (!entryFee && c.startgeld) {
            const feeMatch = c.startgeld.match(/(\d+(?:[,.]\d+)?)\s*(?:€|Euro|EUR)?/);
            if (feeMatch) {
              entryFee = `${feeMatch[1]}€`;
            }
          }
          
          rows.push({ 
            key: String(idx), 
            name: title, 
            date: st.date, 
            time: st.time, 
            entryFee: entryFee || '–',
            raw: c 
          });
        }
      }
      return rows;
    },
    isRecommended(memberId, compKey) {
      const set = this.memberRecommendations[memberId];
      return !!(set && set.has && set.has(compKey));
    },
    toggleRecommendation(memberId, compKey) {
      if (!this.memberRecommendations[memberId]) {
        this.$set ? this.$set(this.memberRecommendations, memberId, new Set()) : (this.memberRecommendations[memberId] = new Set());
      }
      const set = this.memberRecommendations[memberId];
      if (set.has(compKey)) set.delete(compKey); else set.add(compKey);
    },
    async generateMembersPdf() {
      if (!this.selectedMemberIds.length) return;
      const pdf = new PDFGenerator(20, 10, this.$t);
      const title = (this.parsed && this.parsed.parsedData && this.parsed.parsedData.title) ? this.parsed.parsedData.title : 'Offizielles Turnier';
      let first = true;
      for (const mid of this.selectedMemberIds) {
        const m = (this.members || []).find(x => x.id === mid);
        if (!m) continue;
        const rows = this.competitionsForMember(m);
        const recKeys = this.memberRecommendations[mid] ? Array.from(this.memberRecommendations[mid]) : [];
        const recRows = rows.filter(r => recKeys.includes(r.key));
        const otherRows = rows.filter(r => !recKeys.includes(r.key));
        if (!first) pdf.addNewPage();
        first = false;
        const venues = (this.parsed && this.parsed.parsedData && Array.isArray(this.parsed.parsedData.austragungsorte))
          ? this.parsed.parsedData.austragungsorte
          : [];
        pdf.addMemberCompetitions(title, `${m.firstName} ${m.lastName}`, recRows, otherRows, venues);
      }
      pdf.save('turnier_mitglieder.pdf');
    },
    async generateMembersPdfAndClose() {
      await this.generateMembersPdf();
      this.closeMemberDialog();
    },
    // Eligibility: Datum parsen (dd.mm.yyyy | yyyy-mm-dd | ISO)
    parseDateFlexible(s) {
      if (!s || typeof s !== 'string') return null;
      const t = s.trim();
      let m = t.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/);
      if (m) {
        const d = new Date(Number(m[3]), Number(m[2]) - 1, Number(m[1]));
        return isNaN(d.getTime()) ? null : d;
      }
      m = t.match(/^(\d{4})-(\d{2})-(\d{2})$/);
      if (m) {
        const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
        return isNaN(d.getTime()) ? null : d;
      }
      const d = new Date(t);
      return isNaN(d.getTime()) ? null : d;
    },
    parseDateTimeFlexible(s) {
      if (!s || typeof s !== 'string') return null;
      const t = s.trim();
      let m = t.match(/(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?/);
      if (m) {
        const hours = Number(m[4] || 0);
        const minutes = Number(m[5] || 0);
        const d = new Date(Number(m[3]), Number(m[2]) - 1, Number(m[1]), hours, minutes);
        return isNaN(d.getTime()) ? null : d;
      }
      return this.parseDateFlexible(s);
    },
    getCutoffDate(c) {
      let val = c.cutoffDate || c.stichtag || '';
      if (!val && (c.performanceClass || c.leistungsklasse)) {
        const pc = String(c.performanceClass || c.leistungsklasse || '');
        const m = pc.match(/stichtag\s*:?\s*([0-3]?\d\.[01]?\d\.\d{4})/i);
        if (m) val = m[1].trim();
      }
      return this.parseDateFlexible(val);
    },
    getMemberBirthDate(member) {
      return this.parseDateFlexible(member.birthDate || '');
    },
    // Referenzdatum für Altersberechnung: Startzeit-Datum > Turnier-Termin
    getReferenceDate(c) {
      const fromStart = (c.startTime || c.startzeit || '').match(/(\d{1,2}\.\d{1,2}\.\d{4})/);
      if (fromStart) return this.parseDateFlexible(fromStart[1]);
      return this.parseDateFlexible(this.parsed?.parsedData?.termin || '');
    },
    // Uxx, Jugend N, Mädchen N, AK xx erkennen -> { limit, exclusive }
    // exclusive=true: Alter < limit (U12); exclusive=false: Alter <= limit (Jugend 12)
    getAgeLimitFromText(text) {
      if (!text) return null;
      const t = String(text);
      // Jugend 12 / Mädchen 12: max Alter 12 (Alter <= 12)
      let m = t.match(/(?:jugend|mädchen|maedchen)\s+(\d{1,2})\b/i);
      if (m) return { limit: Number(m[1]), exclusive: false };
      // U12: unter 12 (Alter < 12)
      m = t.match(/\bU\s*(\d{1,2})\b/i);
      if (m) return { limit: Number(m[1]), exclusive: true };
      // AK 12 nur wenn nicht in Klammern wie (AK1) - (AK1) ist Kategorie-Nr, nicht Alter
      const akMatch = t.match(/\bAK\s*(\d{1,2})\b/i);
      if (akMatch && !/\(\s*AK\s*\d+\s*\)/i.test(t)) return { limit: Number(akMatch[1]), exclusive: true };
      return null;
    },
    calculateAgeOnDate(birthDate, refDate) {
      if (!birthDate || !refDate) return null;
      let age = refDate.getFullYear() - birthDate.getFullYear();
      const m = refDate.getMonth() - birthDate.getMonth();
      if (m < 0 || (m === 0 && refDate.getDate() < birthDate.getDate())) age--;
      return age;
    },
    getGenderRule(c) {
      const txt = `${c.ageClassCompetition || c.altersklasseWettbewerb || ''} ${c.openTo || c.offenFuer || ''}`.toLowerCase();
      if (/(mädchen|weiblich|\bw\b)/.test(txt)) return 'female';
      if (/(jungen|männlich|\bm\b)/.test(txt)) return 'male';
      if (/jugend/.test(txt)) return 'both';
      return 'both';
    },
    getBirthYearFromPerformanceClass(c) {
      const pc = c.performanceClass || c.leistungsklasse || '';
      const s = String(pc);
      // "Jahrgang 2017 und jünger" -> minBirthYear 2017 (geboren 2017 oder später)
      const mUndJünger = s.match(/jahrgang\s+(\d{4})\s+und\s+jünger/i);
      if (mUndJünger) return { minYear: Number(mUndJünger[1]) };
      // "Jahrgang 2014" -> exakt Jahr 2014
      const mExact = s.match(/jahrgang\s+(\d{4})\b/i);
      if (mExact) return { exact: Number(mExact[1]) };
      return null;
    },
    isEligibleByAge(member, c) {
      const bd = this.getMemberBirthDate(member);
      if (!bd) return false;

      // 1. Jahrgang YYYY (z.B. "Jahrgang 2014") oder "Jahrgang 2017 und jünger"
      const birthYearRule = this.getBirthYearFromPerformanceClass(c);
      if (birthYearRule != null) {
        const memberYear = bd.getFullYear();
        if (birthYearRule.exact != null) return memberYear === birthYearRule.exact;
        if (birthYearRule.minYear != null) return memberYear >= birthYearRule.minYear;
      }

      // 2. Stichtag (z.B. "01.01.2014 und jünger") -> geboren nach Stichtag
      const cutoff = this.getCutoffDate(c);
      if (cutoff) {
        if (!(bd.getTime() > cutoff.getTime())) return false; // jünger = geboren nach Stichtag
      }

      // 3. Alterslimit aus Titel (Jugend 12, U12, AK 12)
      const ageLimit = this.getAgeLimitFromText((c.ageClassCompetition || c.altersklasseWettbewerb || ''));
      if (ageLimit != null) {
        const ref = this.getReferenceDate(c);
        const age = this.calculateAgeOnDate(bd, ref);
        if (age == null) return false;
        if (ageLimit.exclusive) {
          if (!(age < ageLimit.limit)) return false; // U12: Alter < 12
        } else {
          if (!(age <= ageLimit.limit)) return false; // Jugend 12: Alter <= 12
        }
      }
      return true;
    },
    isEligibleForCompetition(member, c) {
      // Geschlecht
      const rule = this.getGenderRule(c);
      const g = member.gender || 'unknown';
      if (rule === 'female' && g !== 'female') return false;
      if (rule === 'male' && g !== 'male') return false;
      // Alter (Stichtag UND/ODER U/AK-Regel; beide greifen, wenn vorhanden)
      if (!this.isEligibleByAge(member, c)) return false;
      return true;
    },
    eligibleMembers(c) { 
      return (this.members || [])
        .filter(m => m.active === true && this.isEligibleForCompetition(m, c))
        .slice()
        .sort((a, b) => this.compareMembers(a, b));
    },
    ageOnRef(member, c) {
      const bd = this.getMemberBirthDate(member);
      const ref = this.getReferenceDate(c);
      return this.calculateAgeOnDate(bd, ref);
    },
    formatDateStr(s) {
      const d = this.parseDateFlexible(s);
      if (!d) return '–';
      const dd = String(d.getDate()).padStart(2, '0');
      const mm = String(d.getMonth() + 1).padStart(2, '0');
      const yyyy = d.getFullYear();
      return `${dd}.${mm}.${yyyy}`;
    },

    startTitleEdit(t) {
      this.editingTournamentId = t.id;
      this.editingTitle = t.title || '';
      this.$nextTick(() => {
        const el = this.$refs.titleEditInput;
        if (Array.isArray(el)) {
          if (el[0]) el[0].focus();
        } else if (el) el.focus();
      });
    },
    cancelTitleEdit() {
      this.editingTournamentId = null;
      this.editingTitle = '';
    },
    async saveTournamentTitle(t) {
      const newTitle = (this.editingTitle || '').trim();
      this.editingTournamentId = null;
      this.editingTitle = '';
      if (newTitle === (t.title || '').trim()) return;
      try {
        await apiClient.patch(`/official-tournaments/${this.currentClub}/${t.id}`, { title: newTitle || null });
        t.title = newTitle || null;
        if (String(this.uploadedId) === String(t.id) && this.parsed) {
          this.parsed.parsedData = { ...this.parsed.parsedData, title: newTitle || this.parsed.parsedData.title };
        }
      } catch (error) {
        this.showInfo(this.$t('common.error'), this.$t('officialTournaments.updateTitleError'), getSafeErrorMessage(error), 'error');
      }
    },
    async removeTournament(t) {
      const confirmed = await this.showConfirm(
        'Turnier löschen',
        `Turnier wirklich löschen?\n${t.title || 'Ohne Titel'} (ID ${t.id})`,
        '',
        'danger'
      );
      if (!confirmed) return;
      await apiClient.delete(`/official-tournaments/${this.currentClub}/${t.id}`);
      if (String(this.uploadedId) === String(t.id)) {
        this.parsed = null;
        this.uploadedId = null;
      }
      await this.loadList();
    },
    suggestMembers(entry) {
      const name = (entry.name || '').toLowerCase();
      const parts = name.split(/\s+/).filter(Boolean);
      return this.members.filter(m => {
        const fn = (m.firstName || '').toLowerCase();
        const ln = (m.lastName || '').toLowerCase();
        const match = parts.some(p => fn.includes(p) || ln.includes(p));
        if (!match) return false;
        if (!entry.genderHint) return true;
        if (entry.genderHint === 'm') return (m.gender || 'unknown') === 'male';
        if (entry.genderHint === 'w') return (m.gender || 'unknown') === 'female';
        return true;
      }).slice(0, 10);
    },
  },
  async mounted() {
    await this.loadList();
    await this.loadClubParticipations();
  }
};
</script>

<style scoped>
.official-tournaments { display: flex; flex-direction: column; gap: 0.75rem; }
.workspace-admin { display: grid; grid-template-columns: minmax(280px, 360px) minmax(0, 1fr); gap: 1rem; align-items: start; }
.admin-panel { background: #fff; border: 1px solid #dbe3f0; border-radius: 12px; padding: .9rem 1rem; box-shadow: 0 4px 18px rgba(15, 35, 95, 0.05); }
.history-panel { margin-top: .25rem; }
.history-toggle { display: flex; justify-content: space-between; gap: 1rem; align-items: center; margin-bottom: .35rem; }
.panel-header { display: flex; flex-direction: column; gap: .2rem; margin-bottom: .75rem; }
.panel-header.compact { margin-bottom: 0; }
.panel-header h3 { margin: 0; color: #17366d; font-size: 1rem; }
.panel-header p { margin: 0; color: #64748b; font-size: .9rem; }
.panel-toolbar { display: flex; align-items: center; gap: .6rem; margin-bottom: .75rem; }
.toolbar-checkbox { display: inline-flex; align-items: center; gap: .35rem; color: #556070; font-size: .9rem; white-space: nowrap; }
.toolbar-meta { color: #64748b; font-size: .88rem; white-space: nowrap; }
.event-list { list-style: none; padding: 0; margin: 0; }
.event-item { display: flex; align-items: center; gap: 0.4rem; padding: .3rem .4rem; border-radius: 8px; }
.event-item.selected { background: #eef4ff; border: 1px solid #d1defd; }
.event-item.is-past { color: #8c96a5; }
.event-item.is-past .event-title,
.event-item.is-past .event-date { color: #8c96a5; }
.event-title { flex: 1; }
.event-date { flex-shrink: 0; }
.list-status-badge { display: inline-flex; align-items: center; padding: .16rem .45rem; border-radius: 999px; font-size: .78rem; font-weight: 600; border: 1px solid transparent; white-space: nowrap; }
.list-status-badge.is-active { background: #e8f1ff; color: #2453a6; border-color: #bfd2ff; }
.list-status-badge.is-upcoming { background: #e6f6ea; color: #1e6b34; border-color: #b9e2c4; }
.list-status-badge.is-past { background: #f2f4f7; color: #556070; border-color: #d8dee8; }
.list-status-badge.is-neutral { background: #fff4db; color: #8a5a00; border-color: #f2d08a; }
.title-input { flex: 1; min-width: 0; padding: 0.25rem 0.4rem; font-size: inherit; }
.btn-icon { background: none; border: none; cursor: pointer; padding: 0.25rem; opacity: 0.7; }
.btn-icon:hover { opacity: 1; }
.workspace-detail { background: #fff; border: 1px solid #dbe3f0; border-radius: 14px; padding: 1rem; box-shadow: 0 4px 18px rgba(15, 35, 95, 0.05); }
.selected-tournament-strip { display: flex; justify-content: space-between; gap: 1rem; align-items: center; flex-wrap: wrap; background: linear-gradient(180deg, #f9fbff 0%, #eef4ff 100%); border: 1px solid #dbe6ff; border-radius: 12px; padding: .8rem 1rem; }
.selected-tournament-main { display: flex; flex-direction: column; gap: .15rem; }
.selected-tournament-label { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: #5f6b85; }
.selected-tournament-meta { display: flex; flex-wrap: wrap; gap: .5rem 1rem; color: #49566c; font-size: .92rem; }
.empty-workspace { display: flex; align-items: center; justify-content: center; min-height: 220px; }
.empty-workspace-card { max-width: 540px; text-align: center; color: #516074; }
.empty-workspace-card h3 { margin: 0 0 .45rem; color: #17366d; }
.empty-workspace-card p { margin: 0; }
.overview-header { display: flex; justify-content: space-between; gap: 1rem; align-items: flex-start; flex-wrap: wrap; }
.overview-main { display: flex; flex-direction: column; gap: .9rem; flex: 1; min-width: min(100%, 520px); }
.workflow-badges { display: flex; flex-wrap: wrap; gap: .45rem; }
.workflow-badge { display: inline-flex; align-items: center; padding: .28rem .55rem; border-radius: 999px; font-size: .82rem; font-weight: 600; border: 1px solid transparent; }
.workflow-badge.is-success { background: #e6f6ea; color: #1e6b34; border-color: #b9e2c4; }
.workflow-badge.is-warning { background: #fff4db; color: #8a5a00; border-color: #f2d08a; }
.workflow-badge.is-danger { background: #fdeaea; color: #962d2d; border-color: #efc1c1; }
.workflow-badge.is-info { background: #e8f1ff; color: #2453a6; border-color: #bfd2ff; }
.workflow-badge.is-neutral { background: #f2f4f7; color: #556070; border-color: #d8dee8; }
.overview-stats { display: grid; grid-template-columns: repeat(4, minmax(120px, 1fr)); gap: .75rem; }
.stat-card { background: linear-gradient(180deg, #f9fbff 0%, #eef4ff 100%); border: 1px solid #dbe6ff; border-radius: 10px; padding: .75rem .9rem; }
.stat-label { display: block; font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: #5f6b85; margin-bottom: .25rem; }
.stat-value { font-size: 1.4rem; color: #17366d; }
.top-actions { display: flex; gap: .5rem; margin-bottom: .5rem; }
.tabs { display: flex; gap: .25rem; border-bottom: 1px solid var(--border-color); margin: .25rem 0 .5rem; }
.tab { background: #f8f9fb; color: var(--text-color, #222); border: none; padding: .4rem .6rem; cursor: pointer; border-bottom: 2px solid transparent; }
.tab:hover { background: #eef1f5; }
.tab.active { border-bottom-color: var(--primary, #2b7cff); font-weight: bold; color: var(--primary, #2b7cff); background: #e9f1ff; }
.filters { display: flex; gap: .5rem; align-items: center; margin: .5rem 0; }
.uploader { display: flex; gap: 0.5rem; align-items: center; }
.empty-state.compact { padding: .25rem 0; }
table { width: 100%; border-collapse: collapse; }
th, td { border-bottom: 1px solid var(--border-color); padding: 0.5rem; text-align: left; }
.comp-details td { background: var(--background-light, #fafafa); }
.details { display: grid; grid-template-columns: 1fr; gap: .4rem 0; padding: .5rem 0; }
.official-tournaments .btn-primary { color: #fff; }
.official-tournaments .btn-secondary { color: #222; }
.official-tournaments .btn-primary:disabled,
.official-tournaments .btn-secondary:disabled { opacity: .6; }
.detail-item { font-size: .95rem; }
.eligible-list { margin-top: .25rem; display: flex; flex-wrap: wrap; gap: .25rem .5rem; }
.eligible-name { background: var(--background, #f1f1f1); border: 1px solid var(--border-color, #ddd); border-radius: 4px; padding: 2px 6px; }
.eligible-table { width: 100%; border-collapse: collapse; margin-top: .25rem; }
.eligible-table th, .eligible-table td { border-bottom: 1px solid var(--border-color); padding: .25rem .4rem; text-align: left; }
.eligible-table .status-cell { text-align: center; vertical-align: middle; }
.eligible-table .action-cell { text-align: center; vertical-align: middle; }
.indented { padding-left: 1.25rem; }
.member-cell { font-weight: 600; vertical-align: top; }
.overview-table td { vertical-align: top; }
.competition-summary { display: flex; flex-wrap: wrap; gap: .3rem; }
.competition-summary-chip { display: inline-flex; align-items: center; padding: .18rem .45rem; border-radius: 999px; background: #f0f4f8; border: 1px solid #d6dde6; font-size: .8rem; color: #415066; }
.competition-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(220px, 1fr)); gap: .45rem .9rem; }
.competition-active-block { margin-top: .85rem; display: flex; flex-direction: column; gap: .55rem; }
.competition-active-list { display: flex; flex-direction: column; gap: .45rem; }
.competition-active-row { display: flex; justify-content: space-between; gap: .8rem; align-items: center; padding: .55rem .65rem; background: #fff; border: 1px solid #d8dee8; border-radius: 8px; }
.competition-active-main { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.competition-active-name { font-weight: 600; }
.competition-active-actions { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.competition-empty-note { margin: 0; color: #6b7280; font-style: italic; }
.competition-chip-list { display: flex; flex-wrap: wrap; gap: .35rem; }
.competition-chip { display: inline-flex; align-items: center; padding: .2rem .45rem; border-radius: 999px; background: #f0f4f8; border: 1px solid #d6dde6; font-size: .83rem; }
.empty-first { border-bottom: 1px solid var(--border-color); }
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.35); display: flex; align-items: center; justify-content: center; z-index: 1000; }
.modal { background: #fff; border-radius: 8px; width: min(800px, 92vw); max-height: 85vh; display: flex; flex-direction: column; box-shadow: 0 10px 30px rgba(0,0,0,.2); }
.modal-header { padding: .75rem 1rem; border-bottom: 1px solid var(--border-color); }
.modal-controls { display: flex; align-items: center; gap: .5rem; padding: .5rem 1rem; border-bottom: 1px solid var(--border-color); }
.modal-body { padding: .75rem 1rem; overflow: auto; }
.checkbox-column { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .5rem .75rem; }
.check-item { display: flex; align-items: center; gap: .35rem; }
.dialog-layout { display: grid; grid-template-columns: 1fr 1.2fr; gap: 1rem; align-items: start; }
.dialog-col h4 { margin: 0 0 .5rem 0; }
.members-col .check-item span.active { font-weight: bold; }
.recommendations-col .check-item { padding: .15rem 0; }

/* Status Management Styles */
.status-cell {
  text-align: center;
  vertical-align: middle;
}

.status-badge {
  display: inline-block;
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-size: 0.85rem;
  font-weight: 500;
  text-align: center;
  min-width: 120px;
}

.status-badge.status-played {
  background-color: #d4edda;
  color: #155724;
  border: 1px solid #c3e6cb;
}

.status-badge.status-registered {
  background-color: #d1ecf1;
  color: #0c5460;
  border: 1px solid #bee5eb;
}

.status-badge.status-wants {
  background-color: #fff3cd;
  color: #856404;
  border: 1px solid #ffeaa7;
}

.status-badge.status-none {
  background-color: #f8d7da;
  color: #721c24;
  border: 1px solid #f5c6cb;
}

.action-cell {
  text-align: center;
  vertical-align: middle;
}

.btn-status {
  padding: 0.4rem 0.8rem;
  border: none;
  border-radius: 4px;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.5s ease;
  min-width: 100px;
}

.btn-status:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.btn-status:active {
  transform: translateY(0);
}

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

.btn-register:hover {
  opacity: 0.95;
}

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

.btn-participate:hover {
  opacity: 0.95;
}

.btn-reset {
  background-color: #6c757d;
  color: white;
}

.btn-reset:hover {
  background-color: #545b62;
}

.placement-input {
  width: 120px;
  padding: 0.25rem 0.5rem;
  border: 1px solid #ced4da;
  border-radius: 4px;
  font-size: 0.85rem;
}

.placement-input:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
}

.no-action {
  color: #6c757d;
  font-style: italic;
  font-size: 0.85rem;
}

/* Entry Fees Styling */
.entry-fees {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.fee-item {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  background-color: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  padding: 0.25rem 0.5rem;
  font-size: 0.9rem;
}

.age-class {
  font-weight: 600;
  color: #495057;
}

.fee-amount {
  font-weight: 700;
  color: #28a745;
}

/* Dialog-spezifische Styles */
.member-dialog-controls {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.member-dialog-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}

@media (max-width: 768px) {
  .workspace-admin {
    grid-template-columns: 1fr;
  }

  .history-toggle {
    flex-direction: column;
    align-items: stretch;
  }

  .panel-toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .overview-stats {
    grid-template-columns: repeat(2, minmax(120px, 1fr));
  }

  .competition-detail-grid {
    grid-template-columns: 1fr;
  }

  .competition-active-row {
    flex-direction: column;
    align-items: flex-start;
  }

  .member-dialog-layout {
    grid-template-columns: 1fr;
  }
}
</style>
