import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const pdfParse = require('pdf-parse/lib/pdf-parse.js');

import { Op } from 'sequelize';
import OfficialTournament from '../models/OfficialTournament.js';
import OfficialCompetition from '../models/OfficialCompetition.js';
import OfficialCompetitionMember from '../models/OfficialCompetitionMember.js';
import Member from '../models/Member.js';
import OfficialTournamentParserService from './officialTournamentParserService.js';

class OfficialTournamentService {
  async uploadTournamentPdf(clubId, pdfBuffer) {
    const data = await pdfParse(pdfBuffer);
    const parsed = OfficialTournamentParserService.parseTournamentText(data.text);

    const tournament = await OfficialTournament.create({
      clubId,
      title: parsed.title || null,
      eventDate: parsed.termin || null,
      organizer: null,
      host: null,
      venues: JSON.stringify(parsed.austragungsorte || []),
      competitionTypes: JSON.stringify(parsed.konkurrenztypen || []),
      registrationDeadlines: JSON.stringify(parsed.meldeschluesse || []),
      entryFees: JSON.stringify(parsed.entryFees || {}),
    });

    for (const c of parsed.competitions || []) {
      const normalizedCompetition = OfficialTournamentParserService.normalizeCompetitionForPersistence(c);
      await OfficialCompetition.create({
        tournamentId: tournament.id,
        ageClassCompetition: normalizedCompetition.altersklasseWettbewerb || normalizedCompetition.ageClassCompetition || null,
        performanceClass: normalizedCompetition.performanceClass || null,
        startTime: normalizedCompetition.startzeit || normalizedCompetition.startTime || null,
        registrationDeadlineDate: normalizedCompetition.meldeschlussDatum || normalizedCompetition.registrationDeadlineDate || null,
        registrationDeadlineOnline: normalizedCompetition.meldeschlussOnline || normalizedCompetition.registrationDeadlineOnline || null,
        cutoffDate: normalizedCompetition.cutoffDate || null,
        ttrRelevant: normalizedCompetition.ttrRelevant || null,
        openTo: normalizedCompetition.offenFuer || normalizedCompetition.openTo || null,
        preliminaryRound: normalizedCompetition.vorrunde || normalizedCompetition.preliminaryRound || null,
        finalRound: normalizedCompetition.endrunde || normalizedCompetition.finalRound || null,
        maxParticipants: normalizedCompetition.maxTeilnehmer || normalizedCompetition.maxParticipants || null,
        entryFee: normalizedCompetition.startgeld || normalizedCompetition.entryFee || null,
      });
    }

    return { id: String(tournament.id) };
  }

  async getParsedTournament(clubId, id) {
    const t = await OfficialTournament.findOne({ where: { id, clubId } });
    if (!t) return null;

    const comps = await OfficialCompetition.findAll({ where: { tournamentId: id } });
    const entries = await OfficialCompetitionMember.findAll({ where: { tournamentId: id } });
    const competitions = comps.map((c) => {
      const j = c.toJSON();
      return {
        id: j.id,
        tournamentId: j.tournamentId,
        ageClassCompetition: j.ageClassCompetition || null,
        performanceClass: j.performanceClass || null,
        startTime: j.startTime || null,
        registrationDeadlineDate: j.registrationDeadlineDate || null,
        registrationDeadlineOnline: j.registrationDeadlineOnline || null,
        cutoffDate: j.cutoffDate || null,
        ttrRelevant: j.ttrRelevant || null,
        openTo: j.openTo || null,
        preliminaryRound: j.preliminaryRound || null,
        finalRound: j.finalRound || null,
        maxParticipants: j.maxParticipants || null,
        entryFee: j.entryFee || null,
        altersklasseWettbewerb: j.ageClassCompetition || null,
        leistungsklasse: j.performanceClass || null,
        startzeit: j.startTime || null,
        meldeschlussDatum: j.registrationDeadlineDate || null,
        meldeschlussOnline: j.registrationDeadlineOnline || null,
        stichtag: j.cutoffDate || null,
        offenFuer: j.openTo || null,
        vorrunde: j.preliminaryRound || null,
        endrunde: j.finalRound || null,
        maxTeilnehmer: j.maxParticipants || null,
        startgeld: j.entryFee || null,
      };
    });

    return {
      id: String(t.id),
      clubId: String(t.clubId),
      parsedData: {
        title: t.title,
        termin: t.eventDate,
        austragungsorte: JSON.parse(t.venues || '[]'),
        konkurrenztypen: JSON.parse(t.competitionTypes || '[]'),
        meldeschluesse: JSON.parse(t.registrationDeadlines || '[]'),
        entryFees: JSON.parse(t.entryFees || '{}'),
        competitions,
      },
      participation: entries.map((e) => ({
        id: e.id,
        tournamentId: e.tournamentId,
        competitionId: e.competitionId,
        memberId: e.memberId,
        wants: !!e.wants,
        registered: !!e.registered,
        participated: !!e.participated,
        placement: e.placement || null,
      })),
    };
  }

  async upsertCompetitionMember(tournamentId, payload) {
    const { competitionId, memberId, wants, registered, participated, placement } = payload;
    if (!competitionId || !memberId) {
      const err = new Error('competitionId and memberId required');
      err.status = 400;
      throw err;
    }

    const [row] = await OfficialCompetitionMember.findOrCreate({
      where: { competitionId, memberId },
      defaults: {
        tournamentId,
        competitionId,
        memberId,
        wants: !!wants,
        registered: !!registered,
        participated: !!participated,
        placement: placement || null,
      },
    });
    row.wants = wants !== undefined ? !!wants : row.wants;
    row.registered = registered !== undefined ? !!registered : row.registered;
    row.participated = participated !== undefined ? !!participated : row.participated;
    if (placement !== undefined) row.placement = placement;
    await row.save();
    return { success: true, id: row.id };
  }

  async updateParticipantStatus(tournamentId, payload) {
    const { competitionId, memberId, action } = payload;
    if (!competitionId || !memberId || !action) {
      const err = new Error('competitionId, memberId and action required');
      err.status = 400;
      throw err;
    }

    const [row] = await OfficialCompetitionMember.findOrCreate({
      where: { competitionId, memberId },
      defaults: {
        tournamentId,
        competitionId,
        memberId,
        wants: false,
        registered: false,
        participated: false,
        placement: null,
      },
    });

    switch (action) {
      case 'register':
        row.wants = true;
        row.registered = true;
        row.participated = false;
        break;
      case 'participate':
        row.wants = true;
        row.registered = true;
        row.participated = true;
        break;
      case 'reset':
        row.wants = true;
        row.registered = false;
        row.participated = false;
        break;
      default: {
        const err = new Error('Invalid action. Use: register, participate, or reset');
        err.status = 400;
        throw err;
      }
    }

    await row.save();
    return {
      success: true,
      id: row.id,
      status: {
        wants: row.wants,
        registered: row.registered,
        participated: row.participated,
        placement: row.placement,
      },
    };
  }

  async listOfficialTournaments(clubId) {
    const list = await OfficialTournament.findAll({ where: { clubId } });
    return Array.isArray(list) ? list : [];
  }

  parseDmy(s) {
    if (!s) return null;
    const m = String(s).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;
  }

  fmtDmy(d) {
    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}`;
  }

  async listClubParticipations(clubId) {
    const tournaments = await OfficialTournament.findAll({ where: { clubId } });
    if (!tournaments || tournaments.length === 0) return [];
    const tournamentIds = tournaments.map((t) => t.id);

    const rows = await OfficialCompetitionMember.findAll({
      where: { tournamentId: { [Op.in]: tournamentIds }, participated: true },
      include: [
        { model: OfficialCompetition, as: 'competition', attributes: ['id', 'tournamentId', 'ageClassCompetition', 'startTime'] },
        { model: OfficialTournament, as: 'tournament', attributes: ['id', 'title', 'eventDate'] },
        { model: Member, as: 'member', attributes: ['id', 'firstName', 'lastName'] },
      ],
    });

    const byTournament = new Map();
    for (const r of rows) {
      const t = r.tournament;
      const c = r.competition;
      const m = r.member;
      if (!t || !c || !m) continue;
      if (!byTournament.has(t.id)) {
        byTournament.set(t.id, {
          tournamentId: String(t.id),
          title: t.title || null,
          startDate: null,
          endDate: null,
          entries: [],
          _dates: [],
          _eventDate: t.eventDate || null,
        });
      }
      const bucket = byTournament.get(t.id);
      const compDate = this.parseDmy(c.startTime || '') || null;
      if (compDate) bucket._dates.push(compDate);
      bucket.entries.push({
        memberId: m.id,
        memberName: `${m.firstName || ''} ${m.lastName || ''}`.trim(),
        competitionId: c.id,
        competitionName: c.ageClassCompetition || '',
        placement: r.placement || null,
        date: compDate ? this.fmtDmy(compDate) : null,
      });
    }

    const out = [];
    for (const t of tournaments) {
      const bucket = byTournament.get(t.id) || {
        tournamentId: String(t.id),
        title: t.title || null,
        startDate: null,
        endDate: null,
        entries: [],
        _dates: [],
        _eventDate: t.eventDate || null,
      };
      if (bucket._dates.length) {
        bucket._dates.sort((a, b) => a - b);
        bucket.startDate = this.fmtDmy(bucket._dates[0]);
        bucket.endDate = this.fmtDmy(bucket._dates[bucket._dates.length - 1]);
      } else if (bucket._eventDate) {
        const all = String(bucket._eventDate).match(/(\d{1,2}\.\d{1,2}\.\d{4})/g) || [];
        if (all.length >= 1) {
          const d1 = this.parseDmy(all[0]);
          const d2 = all.length >= 2 ? this.parseDmy(all[1]) : d1;
          if (d1) bucket.startDate = this.fmtDmy(d1);
          if (d2) bucket.endDate = this.fmtDmy(d2);
        }
      }
      bucket.entries.sort((a, b) => {
        const mcmp = (a.memberName || '').localeCompare(b.memberName || '', 'de', { sensitivity: 'base' });
        if (mcmp !== 0) return mcmp;
        return (a.competitionName || '').localeCompare(b.competitionName || '', 'de', { sensitivity: 'base' });
      });
      delete bucket._dates;
      delete bucket._eventDate;
      out.push(bucket);
    }
    return out;
  }

  async updateOfficialTournament(clubId, id, { title }) {
    const t = await OfficialTournament.findOne({ where: { id, clubId } });
    if (!t) return null;
    if (title !== undefined) t.title = title;
    await t.save();
    return t.toJSON();
  }

  async deleteOfficialTournament(clubId, id) {
    const t = await OfficialTournament.findOne({ where: { id, clubId } });
    if (!t) return false;
    await OfficialCompetition.destroy({ where: { tournamentId: id } });
    await OfficialTournament.destroy({ where: { id } });
    return true;
  }
}

export default new OfficialTournamentService();
