package de.oaa.xxx.games.vanilla.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.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import com.fasterxml.jackson.databind.ObjectMapper;

import de.oaa.xxx.games.common.aufgaben.AufgabenList;
import de.oaa.xxx.games.vanilla.VanillaAufgabeAnzeige;
import de.oaa.xxx.games.vanilla.VanillaGame;
import de.oaa.xxx.games.vanilla.VanillaGameDurchfuehren;
import de.oaa.xxx.games.vanilla.VanillaMitspieler;
import de.oaa.xxx.games.vanilla.entity.VanillaEinladungEntity;
import de.oaa.xxx.games.vanilla.entity.VanillaGameEntity;
import de.oaa.xxx.games.vanilla.entity.VanillaMitspielerEntity;
import de.oaa.xxx.games.vanilla.repository.VanillaEinladungRepository;
import de.oaa.xxx.games.vanilla.repository.VanillaGameRepository;
import de.oaa.xxx.games.vanilla.repository.VanillaMitspielerRepository;
import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.user.UserService;

@RestController
@RequestMapping("/vanilla")
@Transactional
public class VanillaGameController {

    private static final Logger LOGGER = LoggerFactory.getLogger(VanillaGameController.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 VanillaGameRepository sessionRepository;
    private final VanillaMitspielerRepository mitspielerRepository;
    private final VanillaEinladungRepository einladungRepository;
 
    private final ObjectMapper objectMapper;
    private final SystemMessageService systemMessageService;
    private final UserService userService;

    public VanillaGameController(VanillaGameRepository sessionRepository,
                                  VanillaMitspielerRepository mitspielerRepository,
                                  VanillaEinladungRepository einladungRepository,
                                  ObjectMapper objectMapper,
                                  SystemMessageService systemMessageService,
                                  UserService userService) {
        this.sessionRepository = sessionRepository;
        this.mitspielerRepository = mitspielerRepository;
        this.einladungRepository = einladungRepository;
        this.objectMapper = objectMapper;
        this.systemMessageService = systemMessageService;
        this.userService = userService;
    }

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

    @GetMapping
    public ResponseEntity<VanillaGame> getByUserId(@org.springframework.web.bind.annotation.RequestParam UUID userId) {
        return sessionRepository.findByUserId(userId)
                .map(entity -> ResponseEntity.ok(toSession(entity)))
                .orElse(ResponseEntity.noContent().build());
    }

    @PostMapping
    public ResponseEntity<Void> create(@RequestBody VanillaGame session, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();
        var existingOpt = sessionRepository.findByUserId(userId);
        if (existingOpt.isPresent()) {
            VanillaGameEntity existing = existingOpt.get();
            if (existing.getAufgaben() != null) return ResponseEntity.status(409).build();
            // Unvollständige Session (aufgaben=null) bereinigen
            mitspielerRepository.deleteAll(existing.getMitspieler());
            sessionRepository.delete(existing);
        }
        VanillaGameEntity entity = new VanillaGameEntity();
        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.setLevel(1);
        entity.setSetupId(session.getSetupId());
        sessionRepository.save(entity);
        LOGGER.debug("VanillaGame gestartet [sessionId={}, userId={}, aufgabenProLevel={}]",
                entity.getSessionId(), entity.getUserId(), entity.getAufgabenProLevel());
        return ResponseEntity.created(
                ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(entity.getSessionId()).toUri()
        ).build();
    }

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

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

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

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

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

        VanillaMitspielerEntity 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 Vanilla-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));

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

    @PostMapping("/{sessionId}/aufgaben")
    public ResponseEntity<Void> setAufgaben(@RequestBody AufgabenList list, @PathVariable("sessionId") UUID sessionId) {
        try {
            if (list.size() > 1000) {
                return ResponseEntity.badRequest().build();
            }
            String aufgaben = objectMapper.writeValueAsString(list);
            VanillaGameEntity 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() == VanillaEinladungEntity.Status.ACCEPTED_OWN
                                  || e.getStatus() == VanillaEinladungEntity.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<VanillaAufgabeAnzeige> getNextAufgabe(@PathVariable("sessionId") UUID sessionId) {
        try {
            VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null);
            if (session == null || session.getAufgaben() == null) {
                return ResponseEntity.badRequest().build();
            }
            session.setLetzteAktivitaet(LocalDateTime.now());
            VanillaGameDurchfuehren durchfuehren = new VanillaGameDurchfuehren(session);
            VanillaAufgabeAnzeige 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 Vanilla-Aufgabe [sessionId={}, level={}, aufgaben={}/{}]",
                        sessionId, session.getLevel(), session.getAufgabenAufAktuellemLevel(),
                        session.getAufgabenProLevel());
            }
            return ResponseEntity.ok(next);
        } catch (Exception exception) {
            LOGGER.error(exception.getMessage(), exception);
            return ResponseEntity.internalServerError().build();
        }
    }

    @PostMapping("/{sessionId}/mitspieler")
    public ResponseEntity<Void> addMitspieler(@RequestBody VanillaMitspieler mitspieler, @PathVariable("sessionId") UUID sessionId) {
        if (mitspieler.getName() == null || mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) {
            return ResponseEntity.badRequest().build();
        }
        VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) {
            return ResponseEntity.badRequest().build();
        }
        // Max 2 Mitspieler (1 Host + max 1 Gast)
        if (session.getMitspieler().size() >= 2) {
            return ResponseEntity.status(409).build();
        }
        VanillaMitspielerEntity entity = new VanillaMitspielerEntity();
        entity.setMitspielerId(UUID.randomUUID());
        entity.setName(mitspieler.getName());
        entity.setWerkzeuge(new ArrayList<>(mitspieler.getVerfuegbareWerkzeuge()));
        entity.setUserId(mitspieler.getUserId());
        entity.setEigenesGeraet(mitspieler.isEigenesGeraet());
        entity.setSession(session);
        mitspielerRepository.save(entity);
        return ResponseEntity.accepted().build();
    }

    @GetMapping("/{sessionId}/finisher")
    public ResponseEntity<List<VanillaAufgabeAnzeige>> getFinisher(@PathVariable("sessionId") UUID sessionId) {
        try {
            VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null);
            if (session == null) return ResponseEntity.badRequest().build();
            VanillaGameDurchfuehren durchfuehren = new VanillaGameDurchfuehren(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("sessionId") UUID sessionId) {
        try {
            VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null);
            if (session == null) return ResponseEntity.badRequest().build();
            VanillaGameDurchfuehren durchfuehren = new VanillaGameDurchfuehren(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("sessionId") UUID sessionId, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();
        VanillaGameEntity 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 AbschliessenResponse(List<Object> abgelaufeneSperren) {}

    @PostMapping("/{sessionId}/active-task/abschliessen")
    public ResponseEntity<AbschliessenResponse> activeTaskAbschliessen(
            @PathVariable("sessionId") UUID sessionId, @RequestBody AbschliessenRequest req) {
        VanillaGameEntity session = sessionRepository.findById(sessionId).orElse(null);
        if (session == null) return ResponseEntity.notFound().build();
        // Vanilla: keine Sperren/Strafen – einfach activeTask löschen
        session.setActiveTaskJson(null);
        session.setTaskStartedAt(null);
        sessionRepository.save(session);
        return ResponseEntity.ok(new AbschliessenResponse(List.of()));
    }

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

    @PutMapping("/{sessionId}/active-task")
    public ResponseEntity<Void> setActiveTask(@PathVariable("sessionId") UUID sessionId, @RequestBody ActiveTaskRequest req) {
        VanillaGameEntity 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("sessionId") UUID sessionId) {
        VanillaGameEntity 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("sessionId") UUID sessionId) {
        VanillaGameEntity 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));
    }

    // ── Debug-Endpoint: vollständiger Entity-Zustand ──
    @GetMapping("/{sessionId}/debug")
    public ResponseEntity<Map<String, Object>> debug(@PathVariable("sessionId") UUID sessionId) {
        VanillaGameEntity 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("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("werkzeuge",     m.getWerkzeuge());
            mp.put("eigenesGeraet", m.isEigenesGeraet());
            return mp;
        }).toList();

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

    private VanillaGame toSession(VanillaGameEntity entity) {
        VanillaGame session = new VanillaGame();
        session.setSessionId(entity.getSessionId());
        session.setUserId(entity.getUserId());
        session.setAufgabenProLevel(entity.getAufgabenProLevel());
        session.setLevel(entity.getLevel());
        session.setAufgabenAufAktuellemLevel(entity.getAufgabenAufAktuellemLevel());
        session.setStartZeit(entity.getStartZeit());
        session.setLetzteAktivitaet(entity.getLetzteAktivitaet());
        return session;
    }
}
