class OfficialTournamentParserService {
  static normalizeCompetitionForPersistence(c) {
    let performanceClass = c.leistungsklasse || c.performanceClass || null;
    let cutoffDate = c.stichtag || c.cutoffDate || null;

    // "Stichtag" kann auch in Leistungsklasse stehen (z. B. "VR Stichtag 01.01.2014 und jünger")
    if (!cutoffDate && performanceClass && /\bstichtag\b/i.test(performanceClass)) {
      const stichtagMatch = performanceClass.match(
        /stichtag\s*:?\s*([0-3]?\d\.[01]?\d\.\d{4}(?:\s*(?:bis|-)\s*[0-3]?\d\.[01]?\d\.\d{4})?)/i
      );
      if (stichtagMatch) {
        cutoffDate = stichtagMatch[1].trim();
      }
      performanceClass = performanceClass
        .replace(/\bstichtag\b.*$/i, '')
        .replace(/[,:;\-\s]+$/g, '')
        .trim() || null;
    }

    return {
      ...c,
      performanceClass,
      cutoffDate,
    };
  }

  static parseTournamentText(text) {
    const lines = text.split(/\r?\n/);
    const normLines = lines.map((l) => l.replace(/\s+/g, ' ').trim());

    const findTitle = () => {
      // Bevorzugt die Zeile direkt vor "Ausschreibung"
      const ausschreibungIdx = normLines.findIndex((l) => /^Ausschreibung\b/i.test(l));
      if (ausschreibungIdx > 0) {
        for (let i = ausschreibungIdx - 1; i >= 0; i--) {
          const candidate = normLines[i];
          if (!candidate) continue;
          if (/^HTTV\s*\/\s*Kreis/i.test(candidate)) continue;
          if (/^\d+\.\s+/.test(candidate)) continue;
          if (/^nu\.Dokument/i.test(candidate)) continue;
          if (/Ausschreibung\s*\(Fortsetzung\)/i.test(candidate)) continue;
          return candidate;
        }
      }

      // Fallback: typische Turnierbezeichnungen
      const typical = normLines.find(
        (l) =>
          /(meisterschaften|ranglistenturnier|pokal|turnier)/i.test(l) &&
          !/^HTTV\s*\/\s*Kreis/i.test(l) &&
          !/Ausschreibung/i.test(l)
      );
      return typical || null;
    };

    const extractEntryFees = () => {
      const entryFees = {};

      const feePatterns = [
        /startgeld\s*:?\s*(.+)/i,
        /teilnahmegebühr\s*:?\s*(.+)/i,
        /gebühr\s*:?\s*(.+)/i,
        /einschreibegebühr\s*:?\s*(.+)/i,
        /anmeldegebühr\s*:?\s*(.+)/i,
      ];

      for (const pattern of feePatterns) {
        for (let i = 0; i < normLines.length; i++) {
          const line = normLines[i];
          const match = line.match(pattern);
          if (!match) continue;

          const feeText = match[1];
          const feeMatches = feeText.matchAll(
            /(U\d+|AK\s*\d+)\s*:?\s*(\d+(?:[,.]\d+)?)\s*(?:€|Euro|EUR)?/gi
          );

          for (const feeMatch of feeMatches) {
            const ageClass = feeMatch[1].toUpperCase().replace(/\s+/g, '');
            const amount = feeMatch[2].replace(',', '.');
            const numericAmount = parseFloat(amount);

            if (!isNaN(numericAmount)) {
              entryFees[ageClass] = {
                amount: numericAmount,
                currency: '€',
                rawText: feeMatch[0],
              };
            }
          }

          if (Object.keys(entryFees).length > 0) break;
        }
        if (Object.keys(entryFees).length > 0) break;
      }

      return entryFees;
    };

    const extractBlockAfter = (labels, multiline = false) => {
      const idx = normLines.findIndex((l) => labels.some((lb) => l.toLowerCase().startsWith(lb)));
      if (idx === -1) return multiline ? [] : null;
      const line = normLines[idx];
      const afterColon = line.includes(':') ? line.split(':').slice(1).join(':').trim() : '';
      if (!multiline) {
        if (afterColon) return afterColon;
        for (let i = idx + 1; i < normLines.length; i++) {
          if (normLines[i]) return normLines[i];
        }
        return null;
      }
      const out = [];
      if (afterColon) out.push(afterColon);
      for (let i = idx + 1; i < normLines.length; i++) {
        const ln = normLines[i];
        if (!ln) break;
        if (/^(termin|austragungsort|austragungsorte|konkurrenz|konkurrenzen|konkurrenztypen|meldeschluss|altersklassen|startzeiten)/i.test(ln)) break;
        out.push(ln);
      }
      return out;
    };

    const title = findTitle();
    const termin = extractBlockAfter(['termin', 'termin '], false);
    const austragungsorte = extractBlockAfter(['austragungsort', 'austragungsorte'], true);
    let konkurrenzRaw = extractBlockAfter(['konkurrenz', 'konkurrenzen', 'konkurrenztypen'], true);
    if (konkurrenzRaw && !Array.isArray(konkurrenzRaw)) konkurrenzRaw = [konkurrenzRaw];
    const konkurrenztypen = (konkurrenzRaw || [])
      .flatMap((l) => l.split(/[;,]/))
      .map((s) => s.trim())
      .filter(Boolean);

    const meldeschluesseRaw = [];
    for (let i = 0; i < normLines.length; i++) {
      const l = normLines[i];
      const m = l.match(/meldeschluss\s*:?\s*(.+)$/i);
      if (m) meldeschluesseRaw.push({ line: i, value: m[1].trim() });
    }

    let altersRaw = extractBlockAfter(['altersklassen', 'altersklasse'], true);
    if (altersRaw && !Array.isArray(altersRaw)) altersRaw = [altersRaw];
    const altersklassen = (altersRaw || [])
      .flatMap((l) => l.split(/[;,]/))
      .map((s) => s.trim())
      .filter(Boolean);

    const competitions = [];
    const konkIdx = normLines.findIndex((l) => /^\s*3\.?\s+Konkurrenzen/i.test(l) || /^Konkurrenzen\b/i.test(l));
    const startSectionNum = (() => {
      if (konkIdx === -1) return 3;
      const m = normLines[konkIdx].match(/^\s*(\d+)\./);
      return m ? parseInt(m[1], 10) : 3;
    })();

    const nextSectionIdx = () => {
      for (let i = konkIdx + 1; i < normLines.length; i++) {
        const m = normLines[i].match(/^\s*(\d+)\.\s+/);
        if (!m) continue;
        const num = parseInt(m[1], 10);
        if (!Number.isNaN(num) && num > startSectionNum) return i;
      }
      return normLines.length;
    };

    if (konkIdx !== -1) {
      const endIdx = nextSectionIdx();
      let i = konkIdx + 1;
      while (i < endIdx) {
        const line = normLines[i];
        if (!/^Altersklasse\/Wettbewerb\s*:/i.test(line)) {
          i++;
          continue;
        }

        const comp = {};
        comp.altersklasseWettbewerb = line.split(':').slice(1).join(':').trim();
        i++;
        let lastParsedField = null;

        while (i < endIdx && !/^Altersklasse\/Wettbewerb\s*:/i.test(normLines[i])) {
          const ln = normLines[i];
          const m = ln.match(/^([^:]+):\s*(.*)$/);

          if (m) {
            const key = m[1].trim().toLowerCase();
            const val = m[2].trim();
            if (key.startsWith('leistungsklasse')) {
              comp.leistungsklasse = val;
              lastParsedField = 'leistungsklasse';
            } else if (key === 'startzeit') {
              const sm = val.match(/(\d{2}\.\d{2}\.\d{4})\s+(\d{1,2}:\d{2})/);
              comp.startzeit = sm ? `${sm[1]} ${sm[2]}` : val;
              lastParsedField = 'startzeit';
            } else if (key.startsWith('meldeschluss datum')) {
              comp.meldeschlussDatum = val;
              lastParsedField = 'meldeschlussDatum';
            } else if (key.startsWith('meldeschluss online')) {
              comp.meldeschlussOnline = val;
              lastParsedField = 'meldeschlussOnline';
            } else if (key.startsWith('meldeschluss text')) {
              comp.meldeschlussText = val;
              lastParsedField = 'meldeschlussText';
            } else if (key === 'stichtag') {
              comp.stichtag = val;
              lastParsedField = 'stichtag';
            } else if (key === 'ttr-relevant') {
              comp.ttrRelevant = val;
              lastParsedField = 'ttrRelevant';
            } else if (key === 'offen für') {
              comp.offenFuer = val;
              lastParsedField = 'offenFuer';
            } else if (key.startsWith('austragungssys. vorrunde')) {
              comp.vorrunde = val;
              lastParsedField = 'vorrunde';
            } else if (key.startsWith('austragungssys. endrunde')) {
              comp.endrunde = val;
              lastParsedField = 'endrunde';
            } else if (key.startsWith('max. teilnehmerzahl')) {
              comp.maxTeilnehmer = val;
              lastParsedField = 'maxTeilnehmer';
            } else if (key === 'startgeld') {
              comp.startgeld = val;
              lastParsedField = 'startgeld';
            }
          } else if (lastParsedField && ln) {
            const appendableFields = new Set(['leistungsklasse', 'meldeschlussText', 'vorrunde', 'endrunde', 'offenFuer', 'stichtag']);
            if (appendableFields.has(lastParsedField)) {
              const current = comp[lastParsedField];
              comp[lastParsedField] = current ? `${current} ${ln}`.trim() : ln;
            }
          }

          i++;
        }

        competitions.push(comp);
      }
    }

    const akPositions = [];
    for (let i = 0; i < normLines.length; i++) {
      const l = normLines[i];
      const m = l.match(/\b(U\d+|AK\s*\d+)\b/i);
      if (m) akPositions.push({ line: i, ak: m[1].toUpperCase().replace(/\s+/g, '') });
    }

    const meldeschluesseByAk = {};
    for (const ms of meldeschluesseRaw) {
      let best = null;
      let bestDist = Infinity;
      for (const ak of akPositions) {
        const dist = Math.abs(ak.line - ms.line);
        if (dist < bestDist && dist <= 3) {
          best = ak;
          bestDist = dist;
        }
      }
      if (best) {
        if (!meldeschluesseByAk[best.ak]) meldeschluesseByAk[best.ak] = new Set();
        meldeschluesseByAk[best.ak].add(ms.value);
      }
    }

    const meldeschluesse = Array.from(new Set(meldeschluesseRaw.map((x) => x.value)));
    const meldeschluesseByAkOut = Object.fromEntries(
      Object.entries(meldeschluesseByAk).map(([k, v]) => [k, Array.from(v)])
    );

    const entries = [];
    for (const l of normLines) {
      const m = l.match(/^([A-Za-zÄÖÜäöüß\-\s']{3,})(?:\s+\((m|w|d)\))?$/i);
      if (m && /\s/.test(m[1])) {
        entries.push({ name: m[1].trim(), genderHint: m[2] || null });
      }
    }

    const entryFees = extractEntryFees();

    return {
      title,
      termin,
      austragungsorte,
      konkurrenztypen,
      meldeschluesse,
      meldeschluesseByAk: meldeschluesseByAkOut,
      altersklassen,
      startzeiten: {},
      competitions,
      entries,
      entryFees,
      debug: { normLines },
    };
  }
}

export default OfficialTournamentParserService;
