package de.oaa.xxx.games.bdsm.controller;

import java.security.Principal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import de.oaa.xxx.games.bdsm.AufgabeAnzeige;
import de.oaa.xxx.games.bdsm.BdsmGame;
import de.oaa.xxx.games.bdsm.BdsmGameDurchfuehren;
import de.oaa.xxx.games.bdsm.BdsmGameService;
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
import de.oaa.xxx.games.bdsm.BdsmMitspieler;
import de.oaa.xxx.games.common.aufgaben.AufgabenList;
import de.oaa.xxx.games.common.aufgaben.Werkzeug;
import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity;
import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity;
import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity;
import de.oaa.xxx.games.bdsm.entity.MitspielerEntity;
import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository;
import de.oaa.xxx.games.bdsm.repository.BdsmEinladungRepository;
import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository;
import de.oaa.xxx.games.bdsm.repository.MitspielerRepository;
import de.oaa.xxx.games.bdsm.sperre.SperreCallback;
import de.oaa.xxx.games.bdsm.sperre.SperreVerarbeiten;
import de.oaa.xxx.games.bdsm.sperre.SperrenVerlaengernCallback;
import de.oaa.xxx.games.chastity.cardlock.CardLockEntity;
import de.oaa.xxx.games.chastity.cardlock.CardlockRepository;
import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;

@RestController
@RequestMapping("/bdsm")
@Transactional
public class BdsmGameController {

	private static final Logger LOGGER = LoggerFactory.getLogger(BdsmGameController.class);
	/**
	 * Kurzlebiger In-Memory-Marker: Sessions die ordentlich über spielAbgeschlossen
	 * beendet wurden.
	 */
	private static final Set<UUID> ORDENTLICH_BEENDET = Collections.synchronizedSet(new HashSet<>());

	private final BdsmGameRepository sessionRepository;
	private final MitspielerRepository mitspielerRepository;
	private final AktiveSperreRepository aktiveSperreRepository;
	private final UserRepository userRepository;
	private final BdsmEinladungRepository einladungRepository;
	private final ObjectMapper objectMapper;
	private final SystemMessageService systemMessageService;
	private final CardlockRepository cardlockRepository;
	private final BdsmGameService bdsmGameService;
	private final UserService userService;

	public BdsmGameController(BdsmGameRepository sessionRepository, MitspielerRepository mitspielerRepository,
			AktiveSperreRepository aktiveSperreRepository, UserRepository userRepository,
			BdsmEinladungRepository einladungRepository, ObjectMapper objectMapper,
			SystemMessageService systemMessageService, CardlockRepository cardlockRepository,
			BdsmGameService bdsmGameService, UserService userService) {
		this.sessionRepository = sessionRepository;
		this.mitspielerRepository = mitspielerRepository;
		this.aktiveSperreRepository = aktiveSperreRepository;
		this.userRepository = userRepository;
		this.einladungRepository = einladungRepository;
		this.objectMapper = objectMapper;
		this.systemMessageService = systemMessageService;
		this.cardlockRepository = cardlockRepository;
		this.bdsmGameService = bdsmGameService;
		this.userService = userService;
	}

    @GetMapping("/{sessionId}")
    public ResponseEntity<BdsmGame> getBySessionId(@PathVariable UUID sessionId) {
        return sessionRepository.findById(sessionId)
                .map(entity -> ResponseEntity.ok(toSession(entity)))
                .orElse(ResponseEntity.noContent().build());
    }

    @GetMapping
    public ResponseEntity<BdsmGame> getByUserId(@RequestParam UUID userId) {
        return sessionRepository.findByUserId(userId)
                .map(entity -> ResponseEntity.ok(toSession(entity)))
                .orElse(ResponseEntity.noContent().build());
    }

    @PostMapping
    public ResponseEntity<Void> create(@RequestBody BdsmGame session, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();
        var existingOpt = sessionRepository.findByUserId(userId);
        if (existingOpt.isPresent()) {
            BdsmGameEntity existing = existingOpt.get();
            if (existing.getAufgaben() != null) return ResponseEntity.status(409).build();
            // Unvollständige Session (aufgaben=null) bereinigen
            aktiveSperreRepository.deleteAll(existing.getAktiveSperren());
            mitspielerRepository.deleteAll(existing.getMitspieler());
            sessionRepository.delete(existing);
        }
        BdsmGameEntity entity = new BdsmGameEntity();
        entity.setSessionId(UUID.randomUUID());
        entity.setUserId(userId);
        entity.setAufgabenAufAktuellemLevel(0);
        entity.setAufgabenProLevel(session.getAufgabenProLevel() != null ? session.getAufgabenProLevel() : 5);
        LocalDateTime now = LocalDateTime.now();
        entity.setLetzteAktivitaet(now);
        entity.setStartZeit(now);
        entity.setWahrscheinlichkeitSperre(session.getWahrscheinlichkeitSperre() != null ? session.getWahrscheinlichkeitSperre() : 10);
        entity.setWahrscheinlichkeitStrafe(session.getWahrscheinlichkeitStrafe() != null ? session.getWahrscheinlichkeitStrafe() : 10);
        entity.setZeitfaktorZeitstrafen(session.getZeitfaktorZeitstrafen() != null ? session.getZeitfaktorZeitstrafen() : 1.0);
        entity.setLevel(1);
        entity.setSetupId(session.getSetupId());
        sessionRepository.save(entity);
        LOGGER.debug("BdsmGame gestartet [sessionId={}, userId={}, aufgabenProLevel={}, wahrscheinlichkeitStrafe={}%, wahrscheinlichkeitSperre={}%, zeitfaktorZeitstrafen={}]",
                entity.getSessionId(), entity.getUserId(), entity.getAufgabenProLevel(),
                entity.getWahrscheinlichkeitStrafe(), entity.getWahrscheinlichkeitSperre(),
                entity.getZeitfaktorZeitstrafen());
        return ResponseEntity.created(
                ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(entity.getSessionId()).toUri()
        ).build();
    }

    @DeleteMapping
    public ResponseEntity<Void> deleteSession(@RequestBody BdsmGame session) {
        return sessionRepository.findById(session.getSessionId())
                .map(entity -> {
                    aktiveSperreRepository.deleteAll(entity.getAktiveSperren());
                    mitspielerRepository.deleteAll(entity.getMitspieler());
                    sessionRepository.delete(entity);
                    return ResponseEntity.accepted().<Void>build();
                })
                .orElse(ResponseEntity.noContent().build());
    }

    @PostMapping("/{sessionId}/abgeschlossen")
    public ResponseEntity<Void> spielAbgeschlossen(@PathVariable UUID sessionId) {
        BdsmGameEntity entity = sessionRepository.findById(sessionId).orElse(null);
        if (entity == null) return ResponseEntity.notFound().build();
        ORDENTLICH_BEENDET.add(sessionId);
        bdsmGameService.spielAbschliessen(entity);
        return ResponseEntity.accepted().build();
    }

    /** Prüft ob eine Session ordentlich (nicht abgebrochen) beendet wurde. */
    @GetMapping("/{sessionId}/beendet")
    public ResponseEntity<Void> istBeendet(@PathVariable UUID sessionId) {
        if (ORDENTLICH_BEENDET.remove(sessionId)) return ResponseEntity.ok().build();
        return ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{sessionId}/verlassen")
    public ResponseEntity<Void> verlasseSpiel(@PathVariable UUID sessionId, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();

        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();

        MitspielerEntity self = session.getMitspieler().stream()
                .filter(m -> userId.equals(m.getUserId()))
                .findFirst().orElse(null);
        if (self == null) return ResponseEntity.status(403).build();

        String name = self.getName();
        String nachricht = name + " hat das BDSM-Spiel verlassen. Das Spiel wurde abgebrochen.";

        systemMessageService.send(userId, session.getUserId(), nachricht, "/userhome.html", MessageCause.GAME_STATE);
        session.getMitspieler().stream()
                .filter(m -> m.isEigenesGeraet() && m.getUserId() != null && !userId.equals(m.getUserId()))
                .forEach(m -> systemMessageService.send(userId, m.getUserId(), nachricht, "/userhome.html", MessageCause.GAME_STATE));

        aktiveSperreRepository.deleteAll(session.getAktiveSperren());
        mitspielerRepository.deleteAll(session.getMitspieler());
        sessionRepository.delete(session);
        return ResponseEntity.accepted().build();
    }

    @PostMapping("/{sessionId}/aufgaben")
    public ResponseEntity<Void> setAufgaben(@RequestBody AufgabenList list, @PathVariable UUID sessionId) {
        try {
            if (list.size() > 1000) {
                return ResponseEntity.badRequest().build();
            }
            String aufgaben = objectMapper.writeValueAsString(list);
            BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
            if (session == null) {
                return ResponseEntity.badRequest().build();
            }
            session.setAufgaben(aufgaben);
            sessionRepository.save(session);
            // Erst jetzt Einladungen mit der Session verknüpfen – Gäste werden nur weitergeleitet wenn aufgaben bereit sind
            if (session.getSetupId() != null) {
                einladungRepository.findBySetupId(session.getSetupId()).stream()
                        .filter(e -> e.getStatus() == BdsmEinladungEntity.Status.ACCEPTED_OWN
                                  || e.getStatus() == BdsmEinladungEntity.Status.ACCEPTED_HOST)
                        .forEach(e -> e.setSessionId(session.getSessionId()));
            }
            return ResponseEntity.accepted().build();
        } catch (Exception exception) {
            LOGGER.error(exception.getMessage(), exception);
            return ResponseEntity.internalServerError().build();
        }
    }

    @GetMapping("/{sessionId}/aufgaben/next")
    public ResponseEntity<AufgabeAnzeige> getNextAufgabe(@PathVariable UUID sessionId) {
        try {
            BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
            if (session == null || session.getAufgaben() == null) {
                return ResponseEntity.badRequest().build();
            }
            session.setLetzteAktivitaet(LocalDateTime.now());
            BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session);
            AufgabeAnzeige next = durchfuehren.getNext();
            session.setLevel(durchfuehren.getLevel());
            session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel());
            if (next == null) {
                return ResponseEntity.noContent().build();
            }
            next.setLevel(durchfuehren.getLevel());
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Neue Aufgabe [sessionId={}, level={}, aufgaben={}/{}, aktiveSperren={}]",
                        sessionId, session.getLevel(), session.getAufgabenAufAktuellemLevel(),
                        session.getAufgabenProLevel(), session.getAktiveSperren().size());
                session.getAktiveSperren().forEach(s ->
                        LOGGER.debug("  Sperre [mitspieler={}, {}min, ende={}]",
                                s.getMitspieler().getName(), s.getMinuten(), s.getEndzeit()));
            }
            return ResponseEntity.ok(next);
        } catch (Exception exception) {
            LOGGER.error(exception.getMessage(), exception);
            return ResponseEntity.internalServerError().build();
        }
    }

    @PostMapping("/{sessionId}/mitspieler")
    public ResponseEntity<Void> addMitspieler(@RequestBody BdsmMitspieler mitspieler, @PathVariable UUID sessionId) {
        if (mitspieler.getName() == null || mitspieler.getGeschlecht() == null || mitspieler.getRollen() == null
                || mitspieler.getRollen().isEmpty() || mitspieler.getSpieltMit() == null || mitspieler.getSpieltMit().isEmpty()
                || mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) {
            return ResponseEntity.badRequest().build();
        }
        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) {
            return ResponseEntity.badRequest().build();
        }
        MitspielerEntity entity = new MitspielerEntity();
        entity.setMitspielerId(UUID.randomUUID());
        entity.setGeschlecht(mitspieler.getGeschlecht());
        entity.setName(mitspieler.getName());
        entity.setRollen(mitspieler.getRollen());
        entity.setSpieltMit(mitspieler.getSpieltMit());
        entity.setWerkzeuge(new ArrayList<>(mitspieler.getVerfuegbareWerkzeuge()));
        entity.setUserId(mitspieler.getUserId());
        entity.setEigenesGeraet(mitspieler.isEigenesGeraet());
        entity.setSperrenVorFinaleAufloesen(mitspieler.isSperrenVorFinaleAufloesen());
        entity.setSession(session);
        mitspielerRepository.save(entity);

        // Aktive Chastity-Lockees: 365-Tage-Zeitstrafe auf das gesperrte Körperteil
        if (mitspieler.getUserId() != null
                && cardlockRepository.existsByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(mitspieler.getUserId())) {
            List<Werkzeug> locked = new ArrayList<>();
            if (mitspieler.getGeschlecht() == GeschlechtEnum.WEIBLICH)       locked.add(Werkzeug.VAGINA);
            else if (mitspieler.getGeschlecht() == GeschlechtEnum.MAENNLICH) locked.add(Werkzeug.PENIS);
            else { locked.add(Werkzeug.VAGINA); locked.add(Werkzeug.PENIS); }

            if (!locked.isEmpty()) {
                // Gesperrte Werkzeuge force-hinzufügen (auch wenn Checkbox nicht angekreuzt)
                locked.forEach(w -> { if (!entity.getWerkzeuge().contains(w)) entity.getWerkzeuge().add(w); });
                LocalDateTime now = LocalDateTime.now();
                AktiveSperreEntity chastitySperre = new AktiveSperreEntity();
                chastitySperre.setAktiveSperreId(UUID.randomUUID());
                chastitySperre.setMitspieler(entity);
                chastitySperre.setSession(session);
                chastitySperre.setFuer(locked);
                chastitySperre.setMinuten(1440);
                chastitySperre.setStartzeit(now);
                chastitySperre.setEndzeit(now.plusHours(24));
                chastitySperre.setReleaseText(entity.getName() + " hat die Keuschheit durchgehalten – das Schloss ist ab sofort offen.");
                aktiveSperreRepository.save(chastitySperre);
                // Werkzeug für die Spieldauer durch die Zeitstrafe sperren
                locked.forEach(entity.getWerkzeuge()::remove);
                mitspielerRepository.save(entity);
            }
        }

        return ResponseEntity.accepted().build();
    }

    @GetMapping("/{sessionId}/finisher")
    public ResponseEntity<List<AufgabeAnzeige>> getFinisher(@PathVariable UUID sessionId) {
        try {
            BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
            if (session == null) return ResponseEntity.badRequest().build();
            BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session);
            return ResponseEntity.ok(durchfuehren.getFinisher());
        } catch (Exception exception) {
            LOGGER.error(exception.getMessage(), exception);
            return ResponseEntity.internalServerError().build();
        }
    }

    @PostMapping("/{sessionId}/backToLevel5")
    public ResponseEntity<Void> backToLevel5(@PathVariable UUID sessionId) {
        try {
            BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
            if (session == null) return ResponseEntity.badRequest().build();
            BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session);
            durchfuehren.backToLvl5();
            session.setLevel(durchfuehren.getLevel());
            session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel());
            sessionRepository.save(session);
            return ResponseEntity.accepted().build();
        } catch (Exception exception) {
            LOGGER.error(exception.getMessage(), exception);
            return ResponseEntity.internalServerError().build();
        }
    }

    @GetMapping("/{sessionId}/mitspieler/me")
    public ResponseEntity<Map<String, Object>> getMeinMitspieler(@PathVariable UUID sessionId, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();
        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();
        return session.getMitspieler().stream()
                .filter(m -> userId.equals(m.getUserId()))
                .findFirst()
                .map(m -> {
                    Map<String, Object> result = new LinkedHashMap<>();
                    result.put("mitspielerId", m.getMitspielerId());
                    result.put("name", m.getName());
                    result.put("eigenesGeraet", m.isEigenesGeraet());
                    return ResponseEntity.ok(result);
                })
                .orElse(ResponseEntity.noContent().build());
    }

    record AbschliessenRequest(boolean sperreAnwenden) {}
    record SperreFreigabe(String text, UUID mitspielerId, boolean eigenesGeraet) {}
    record AbschliessenResponse(List<SperreFreigabe> abgelaufeneSperren) {}

    @PostMapping("/{sessionId}/active-task/abschliessen")
    public ResponseEntity<AbschliessenResponse> activeTaskAbschliessen(
            @PathVariable UUID sessionId, @RequestBody AbschliessenRequest req) {
        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();

        SperreVerarbeiten sperreVerarbeiten = new SperreVerarbeiten();

        if (req.sperreAnwenden() && session.getActiveTaskJson() != null) {
            try {
                JsonNode task = objectMapper.readTree(session.getActiveTaskJson());
                JsonNode cb = task.get("callback");
                if (cb != null && !cb.isNull()) {
                    if (cb.has("sperreId") && !cb.get("sperreId").isNull()) {
                        SperreCallback callback = objectMapper.treeToValue(cb, SperreCallback.class);
                        callback.setSessionId(sessionId);
                        sperreVerarbeiten.sperreAnwenden(callback, sessionRepository, mitspielerRepository, aktiveSperreRepository);
                        LOGGER.info("Zeitstrafe via abschliessen angewandt [session={}, spieler={}]", sessionId, callback.getSpielerId());
                    } else if (cb.has("faktor") && !cb.get("faktor").isNull()) {
                        SperrenVerlaengernCallback callback = objectMapper.treeToValue(cb, SperrenVerlaengernCallback.class);
                        List<AktiveSperreEntity> locks = aktiveSperreRepository.findAktiveLocks(callback.getSpielerId());
                        locks.forEach(lock -> sperreVerarbeiten.sperreVerlaengern(lock, callback.getFaktor(), aktiveSperreRepository));
                        LOGGER.info("Sperren via abschliessen verlängert [session={}, spieler={}, faktor={}]", sessionId, callback.getSpielerId(), callback.getFaktor());
                    }
                }
            } catch (Exception e) {
                LOGGER.error("Fehler beim Verarbeiten des Callbacks beim Abschließen: {}", e.getMessage(), e);
            }
        }

        session.setActiveTaskJson(null);
        session.setTaskStartedAt(null);
        sessionRepository.save(session);

        List<SperreFreigabe> freigaben = new ArrayList<>();
        aktiveSperreRepository.findAbgelaufene(sessionId, LocalDateTime.now()).forEach(s -> {
            UUID mitspielerId = s.getMitspieler().getMitspielerId();
            boolean eigenesGeraet = s.getMitspieler().isEigenesGeraet();
            String t = sperreVerarbeiten.sperreAufheben(s, aktiveSperreRepository, mitspielerRepository);
            if (t != null && !t.isBlank()) freigaben.add(new SperreFreigabe(t, mitspielerId, eigenesGeraet));
        });

        return ResponseEntity.ok(new AbschliessenResponse(freigaben));
    }

    record ActiveTaskRequest(String taskJson, LocalDateTime timerStartedAt) {}
    record ActiveTaskResponse(String taskJson, Long elapsedSeconds) {}

    @PutMapping("/{sessionId}/active-task")
    public ResponseEntity<Void> setActiveTask(@PathVariable UUID sessionId, @RequestBody ActiveTaskRequest req) {
        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();
        session.setActiveTaskJson(req.taskJson());
        session.setTaskStartedAt(req.timerStartedAt());
        sessionRepository.save(session);
        return ResponseEntity.accepted().build();
    }

    @DeleteMapping("/{sessionId}/active-task")
    public ResponseEntity<Void> clearActiveTask(@PathVariable UUID sessionId) {
        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();
        session.setActiveTaskJson(null);
        session.setTaskStartedAt(null);
        sessionRepository.save(session);
        return ResponseEntity.accepted().build();
    }

    @GetMapping("/{sessionId}/active-task")
    public ResponseEntity<ActiveTaskResponse> getActiveTask(@PathVariable UUID sessionId) {
        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();
        if (session.getActiveTaskJson() == null) return ResponseEntity.noContent().build();
        Long elapsed = null;
        if (session.getTaskStartedAt() != null) {
            elapsed = Duration.between(session.getTaskStartedAt(), LocalDateTime.now()).getSeconds();
        }
        return ResponseEntity.ok(new ActiveTaskResponse(session.getActiveTaskJson(), elapsed));
    }

    // ── Keyholder-Angebot: prüft ob am Ende eine VAGINA/PENIS-Sperre vorliegt ──
    @GetMapping("/{sessionId}/keyholder-angebot")
    public ResponseEntity<Map<String, Object>> keyholderAngebot(@PathVariable UUID sessionId) {
        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();

        // Alle noch in der DB vorhandenen VAGINA/PENIS-Sperren – auch abgelaufene,
        // da im Finale-Flow bereits abgelaufene Sperren noch nicht formal aufgehoben wurden.
        List<AktiveSperreEntity> relevantesSperren = session.getAktiveSperren().stream()
                .filter(s -> s.getFuer().contains(Werkzeug.VAGINA) || s.getFuer().contains(Werkzeug.PENIS))
                .toList();

        for (AktiveSperreEntity sperre : relevantesSperren) {
            MitspielerEntity lockee = sperre.getMitspieler();
            if (lockee == null || lockee.getUserId() == null || lockee.getGeschlecht() == null) continue;
            // Kein Angebot wenn Lockee bereits aktiv in einem Chastity-Game gesperrt ist
            if (cardlockRepository.existsByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(lockee.getUserId())) continue;

            for (MitspielerEntity kandidat : session.getMitspieler()) {
                if (kandidat.getMitspielerId().equals(lockee.getMitspielerId())) continue;
                if (kandidat.getUserId() == null) continue;
                if (!kandidat.getSpieltMit().contains(lockee.getGeschlecht())) continue;
                List<CardLockEntity> locks = cardlockRepository.findByKeyholderAndUnlockTimeIsNull(kandidat.getUserId());
                if (locks.isEmpty()) continue;

                Map<String, Object> result = new LinkedHashMap<>();
                result.put("lockeeId",               lockee.getMitspielerId());
                result.put("lockeeName",             lockee.getName());
                result.put("lockeeUserId",           lockee.getUserId());
                result.put("keyholderMitspielerId",  kandidat.getMitspielerId());
                result.put("keyholderName",          kandidat.getName());
                result.put("keyholderUserId",        kandidat.getUserId());
                return ResponseEntity.ok(result);
            }
        }
        return ResponseEntity.noContent().build();
    }

    @GetMapping("/{sessionId}/keyholder-locks")
    public ResponseEntity<List<Map<String, Object>>> keyholderLocks(
            @PathVariable UUID sessionId, @RequestParam UUID keyholderUserId) {
        BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();

        List<Map<String, Object>> result = cardlockRepository.findByKeyholderAndUnlockTimeIsNull(keyholderUserId).stream()
                .map(l -> {
                    Map<String, Object> item = new LinkedHashMap<>();
                    item.put("lockId",          l.getLockId());
                    item.put("name",            l.getName() != null ? l.getName() : "Unbenanntes Lock");
                    item.put("pickEveryMinute", l.getPickEveryMinute());
                    item.put("totalCards",      l.getInitialCards() != null ? l.getInitialCards().size() : 0);
                    item.put("active",          l.getStartTime() != null && l.getUnlockTime() == null);
                    return item;
                })
                .toList();

        if (result.isEmpty()) return ResponseEntity.noContent().build();
        return ResponseEntity.ok(result);
    }

    record ZuChastityRequest(UUID lockId, UUID lockeeUserId, UUID keyholderUserId) {}

    @PostMapping("/{sessionId}/zu-chastity")
    public ResponseEntity<Map<String, Object>> zuChastity(
            @PathVariable UUID sessionId, @RequestBody ZuChastityRequest req) {
        try {
            CardLockEntity newLock = bdsmGameService.zuChastity(
                    sessionId, req.lockId(), req.lockeeUserId(), req.keyholderUserId());
            Map<String, Object> response = new LinkedHashMap<>();
            response.put("lockId", newLock.getLockId().toString());
            response.put("unlockCode", newLock.getUnlockCode());
            return ResponseEntity.ok(response);
        } catch (IllegalArgumentException e) {
            String msg = e.getMessage();
            if (msg != null && msg.contains("Session")) return ResponseEntity.notFound().build();
            return ResponseEntity.badRequest().build();
        } catch (IllegalStateException e) {
            return ResponseEntity.status(409).build();
        }
    }

    /** Gibt zurück welches Werkzeug für einen User durch ein aktives Chastity-Lock blockiert ist. */
    @GetMapping("/chastity-constraint")
    public ResponseEntity<Map<String, Object>> chastityConstraint(@RequestParam UUID userId) {
        Map<String, Object> result = new LinkedHashMap<>();
        if (!cardlockRepository.existsByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(userId)) {
            result.put("lockedWerkzeug", null);
            return ResponseEntity.ok(result);
        }
        return userRepository.findById(userId).map(u -> {
            String werkzeug = null;
            if (u.getGeschlecht() != null) {
                werkzeug = switch (u.getGeschlecht().name()) {
                    case "WEIBLICH"  -> "VAGINA";
                    case "MAENNLICH" -> "PENIS";
                    default          -> "BOTH";
                };
            }
            result.put("lockedWerkzeug", werkzeug);
            return ResponseEntity.ok(result);
        }).orElseGet(() -> {
            result.put("lockedWerkzeug", null);
            return ResponseEntity.ok(result);
        });
    }

    // ── Debug-Endpoint: vollständiger Entity-Zustand ──
    @GetMapping("/{sessionId}/debug")
    public ResponseEntity<Map<String, Object>> debug(@PathVariable UUID sessionId) {
        BdsmGameEntity entity = sessionRepository.findById(sessionId).orElse(null);
        if (entity == null) return ResponseEntity.notFound().build();

        Map<String, Object> session = new LinkedHashMap<>();
        session.put("sessionId",               entity.getSessionId());
        session.put("userId",                  entity.getUserId());
        session.put("setupId",                 entity.getSetupId());
        session.put("startZeit",               entity.getStartZeit());
        session.put("letzteAktivitaet",        entity.getLetzteAktivitaet());
        session.put("level",                   entity.getLevel());
        session.put("aufgabenAufAktuellemLevel", entity.getAufgabenAufAktuellemLevel());
        session.put("aufgabenProLevel",        entity.getAufgabenProLevel());
        session.put("wahrscheinlichkeitSperre",  entity.getWahrscheinlichkeitSperre());
        session.put("wahrscheinlichkeitStrafe",  entity.getWahrscheinlichkeitStrafe());
        session.put("zeitfaktorZeitstrafen",   entity.getZeitfaktorZeitstrafen());
        session.put("taskStartedAt",           entity.getTaskStartedAt());
        session.put("hatAufgaben",             entity.getAufgaben() != null);
        session.put("hatActiveTask",           entity.getActiveTaskJson() != null);

        List<Map<String, Object>> mitspielerList = entity.getMitspieler().stream().map(m -> {
            Map<String, Object> mp = new LinkedHashMap<>();
            mp.put("mitspielerId",             m.getMitspielerId());
            mp.put("name",                     m.getName());
            mp.put("userId",                   m.getUserId());
            mp.put("geschlecht",               m.getGeschlecht());
            mp.put("rollen",                   m.getRollen());
            mp.put("werkzeuge",                m.getWerkzeuge());
            mp.put("spieltMit",                m.getSpieltMit());
            mp.put("eigenesGeraet",            m.isEigenesGeraet());
            mp.put("sperrenVorFinaleAufloesen", m.isSperrenVorFinaleAufloesen());
            return mp;
        }).toList();

        LocalDateTime now = LocalDateTime.now();
        List<Map<String, Object>> sperrenList = entity.getAktiveSperren().stream().map(s -> {
            Map<String, Object> sp = new LinkedHashMap<>();
            sp.put("aktiveSperreId",  s.getAktiveSperreId());
            sp.put("mitspielerName",  s.getMitspieler() != null ? s.getMitspieler().getName() : null);
            sp.put("fuer",            s.getFuer());
            sp.put("minuten",         s.getMinuten());
            sp.put("startzeit",       s.getStartzeit());
            sp.put("endzeit",         s.getEndzeit());
            sp.put("abgelaufen",      s.getEndzeit() != null && s.getEndzeit().isBefore(now));
            sp.put("releaseText",     s.getReleaseText());
            return sp;
        }).toList();

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("session",      session);
        result.put("mitspieler",   mitspielerList);
        result.put("aktiveSperren", sperrenList);
        return ResponseEntity.ok(result);
    }

    private BdsmGame toSession(BdsmGameEntity entity) {
        BdsmGame session = new BdsmGame();
        session.setSessionId(entity.getSessionId());
        session.setUserId(entity.getUserId());
        session.setAufgabenProLevel(entity.getAufgabenProLevel());
        session.setWahrscheinlichkeitSperre(entity.getWahrscheinlichkeitSperre());
        session.setWahrscheinlichkeitStrafe(entity.getWahrscheinlichkeitStrafe());
        session.setZeitfaktorZeitstrafen(entity.getZeitfaktorZeitstrafen());
        session.setLevel(entity.getLevel());
        session.setAufgabenAufAktuellemLevel(entity.getAufgabenAufAktuellemLevel());
        session.setStartZeit(entity.getStartZeit());
        session.setLetzteAktivitaet(entity.getLetzteAktivitaet());
        return session;
    }
}
