package de.oaa.xxx.gruppe;

import de.oaa.xxx.gruppe.dto.*;
import de.oaa.xxx.gruppe.entity.*;
import de.oaa.xxx.gruppe.repository.*;
import de.oaa.xxx.social.LikeService;
import de.oaa.xxx.social.repository.KommentarRepository;
import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
import java.time.LocalDateTime;
import java.util.*;

@RestController
public class GruppenbeitragController {

    private static final Logger LOGGER = LoggerFactory.getLogger(GruppenbeitragController.class);

    private final GruppeRepository gruppeRepository;
    private final GruppenmitgliedRepository mitgliedRepository;
    private final GruppenbeitragRepository beitragRepository;
    private final UmfrageOptionRepository optionRepository;
    private final UmfrageStimmeRepository stimmeRepository;
    private final GruppenbeitragLikeRepository likeRepository;
    private final BeitragMeldungRepository meldungRepository;
    private final KommentarRepository kommentarRepository;
    private final UserRepository userRepository;
    private final UserService userService;
    private final LikeService likeService;

    public GruppenbeitragController(GruppeRepository gruppeRepository,
                                     GruppenmitgliedRepository mitgliedRepository,
                                     GruppenbeitragRepository beitragRepository,
                                     UmfrageOptionRepository optionRepository,
                                     UmfrageStimmeRepository stimmeRepository,
                                     GruppenbeitragLikeRepository likeRepository,
                                     BeitragMeldungRepository meldungRepository,
                                     KommentarRepository kommentarRepository,
                                     UserRepository userRepository,
                                     UserService userService,
                                     LikeService likeService) {
        this.gruppeRepository = gruppeRepository;
        this.mitgliedRepository = mitgliedRepository;
        this.beitragRepository = beitragRepository;
        this.optionRepository = optionRepository;
        this.stimmeRepository = stimmeRepository;
        this.likeRepository = likeRepository;
        this.meldungRepository = meldungRepository;
        this.kommentarRepository = kommentarRepository;
        this.userRepository = userRepository;
        this.userService = userService;
        this.likeService = likeService;
    }

    record CreateBeitragRequest(String beitragTyp, String text, Boolean multiChoice, List<String> optionen, List<String> bilder) {}
    record PostsPage(List<GruppenbeitragDto> posts, boolean hasMore) {}
    record VoteRequest(UUID optionId) {}
    record ReportRequest(String grund) {}

    // ── GET /gruppen/{id}/posts/{postId} ──

    @GetMapping("/gruppen/{id}/posts/{postId}")
    public ResponseEntity<GruppenbeitragDto> getPost(@PathVariable("id") UUID id,
                                                      @PathVariable("postId") UUID postId,
                                                      Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isEmpty())
            return ResponseEntity.status(403).build();
        return beitragRepository.findById(postId)
                .map(b -> ResponseEntity.ok(toDto(b, myId)))
                .orElse(ResponseEntity.notFound().build());
    }

    // ── GET /gruppen/{id}/posts ──

    @GetMapping("/gruppen/{id}/posts")
    public ResponseEntity<PostsPage> getPosts(@PathVariable("id") UUID id,
                                               @RequestParam(name = "page", defaultValue = "0") int page,
                                               @RequestParam(name = "size", defaultValue = "10") int size,
                                               Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isEmpty())
            return ResponseEntity.status(403).build();

        Slice<GruppenbeitragEntity> slice = beitragRepository
                .findByGruppeIdOrderByCreatedAtDesc(id, PageRequest.of(page, size));
        List<GruppenbeitragDto> posts = slice.getContent().stream()
                .map(b -> toDto(b, myId))
                .toList();
        return ResponseEntity.ok(new PostsPage(posts, slice.hasNext()));
    }

    // ── POST /gruppen/{id}/posts ──

    @PostMapping("/gruppen/{id}/posts")
    public ResponseEntity<GruppenbeitragDto> createPost(@PathVariable("id") UUID id,
                                                         @RequestBody CreateBeitragRequest req,
                                                         Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isEmpty())
            return ResponseEntity.status(403).build();
        if (gruppeRepository.findById(id).isEmpty()) return ResponseEntity.notFound().build();
        if (req.text() == null || req.text().isBlank()) return ResponseEntity.badRequest().build();

        BeitragTyp typ;
        try {
            typ = BeitragTyp.valueOf(req.beitragTyp());
        } catch (Exception e) {
            return ResponseEntity.badRequest().build();
        }

        GruppenbeitragEntity beitrag = new GruppenbeitragEntity();
        beitrag.setBeitragId(UUID.randomUUID());
        beitrag.setGruppeId(id);
        beitrag.setAuthorId(myId);
        beitrag.setBeitragTyp(typ);
        beitrag.setText(req.text().trim());
        beitrag.setMultiChoice(typ == BeitragTyp.UMFRAGE ? req.multiChoice() : null);
        beitrag.setBilder(req.bilder() != null ? req.bilder() : List.of());
        beitrag.setCreatedAt(LocalDateTime.now());
        beitragRepository.save(beitrag);
        LOGGER.debug("User {} hat Beitrag {} (Typ: {}) in Gruppe {} erstellt", myId, beitrag.getBeitragId(), typ, id);

        if (typ == BeitragTyp.UMFRAGE && req.optionen() != null) {
            for (int i = 0; i < req.optionen().size(); i++) {
                String optText = req.optionen().get(i);
                if (optText == null || optText.isBlank()) continue;
                UmfrageOptionEntity opt = new UmfrageOptionEntity();
                opt.setOptionId(UUID.randomUUID());
                opt.setBeitragId(beitrag.getBeitragId());
                opt.setText(optText.trim());
                opt.setReihenfolge(i);
                optionRepository.save(opt);
            }
        }

        return ResponseEntity.status(201).body(toDto(beitrag, myId));
    }

    // ── DELETE /gruppen/{id}/posts/{postId} ──

    @DeleteMapping("/gruppen/{id}/posts/{postId}")
    public ResponseEntity<Void> deletePost(@PathVariable("id") UUID id,
                                            @PathVariable("postId") UUID postId,
                                            Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        var bOpt = beitragRepository.findById(postId);
        if (bOpt.isEmpty()) return ResponseEntity.notFound().build();
        GruppenbeitragEntity beitrag = bOpt.get();

        boolean isAuthor = beitrag.getAuthorId().equals(myId);
        boolean isAdmin = mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId)
                .map(m -> m.getRolle() == GruppenRolle.ADMIN)
                .orElse(false);
        if (!isAuthor && !isAdmin) return ResponseEntity.status(403).build();

        deleteBeitragCascade(beitrag);
        LOGGER.info("User {} hat Beitrag {} aus Gruppe {} gelöscht", myId, postId, id);
        return ResponseEntity.noContent().build();
    }

    // ── POST /gruppen/{id}/posts/{postId}/like ──

    @PostMapping("/gruppen/{id}/posts/{postId}/like")
    public ResponseEntity<Void> toggleLike(@PathVariable("id") UUID id,
                                            @PathVariable("postId") UUID postId,
                                            Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isEmpty())
            return ResponseEntity.status(403).build();

        likeService.toggleGruppenbeitragLike(postId, myId);
        return ResponseEntity.ok().build();
    }

    // ── POST /gruppen/{id}/posts/{postId}/vote ──

    @PostMapping("/gruppen/{id}/posts/{postId}/vote")
    public ResponseEntity<Void> vote(@PathVariable("id") UUID id,
                                      @PathVariable("postId") UUID postId,
                                      @RequestBody VoteRequest req,
                                      Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isEmpty())
            return ResponseEntity.status(403).build();

        var bOpt = beitragRepository.findById(postId);
        if (bOpt.isEmpty()) return ResponseEntity.notFound().build();
        GruppenbeitragEntity beitrag = bOpt.get();

        var optOpt = optionRepository.findById(req.optionId());
        if (optOpt.isEmpty() || !optOpt.get().getBeitragId().equals(postId))
            return ResponseEntity.badRequest().build();

        boolean isMultiChoice = Boolean.TRUE.equals(beitrag.getMultiChoice());

        var existingVote = stimmeRepository.findByOptionIdAndUserId(req.optionId(), myId);
        if (existingVote.isPresent()) {
            // Toggle: remove vote
            stimmeRepository.delete(existingVote.get());
            return ResponseEntity.ok().build();
        }

        if (!isMultiChoice) {
            // Single-choice: remove all existing votes on this poll
            List<UmfrageStimmeEntity> existing = stimmeRepository.findByBeitragIdAndUserId(postId, myId);
            stimmeRepository.deleteAll(existing);
        }

        UmfrageStimmeEntity stimme = new UmfrageStimmeEntity();
        stimme.setStimmeId(UUID.randomUUID());
        stimme.setOptionId(req.optionId());
        stimme.setBeitragId(postId);
        stimme.setUserId(myId);
        stimmeRepository.save(stimme);
        LOGGER.debug("User {} hat für Option {} in Beitrag {} gestimmt", myId, req.optionId(), postId);
        return ResponseEntity.ok().build();
    }

    // ── POST /gruppen/{id}/posts/{postId}/report ──

    @PostMapping("/gruppen/{id}/posts/{postId}/report")
    public ResponseEntity<Void> report(@PathVariable("id") UUID id,
                                        @PathVariable("postId") UUID postId,
                                        @RequestBody ReportRequest req,
                                        Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isEmpty())
            return ResponseEntity.status(403).build();

        if (beitragRepository.findById(postId).isEmpty()) return ResponseEntity.notFound().build();

        var existing = meldungRepository.findByBeitragIdAndMelderId(postId, myId);
        if (existing.isPresent()) return ResponseEntity.status(409).build();

        BeitragMeldungEntity meldung = new BeitragMeldungEntity();
        meldung.setMeldungId(UUID.randomUUID());
        meldung.setBeitragId(postId);
        meldung.setMelderId(myId);
        meldung.setGrund(req.grund());
        meldung.setGemeldetAt(LocalDateTime.now());
        meldungRepository.save(meldung);
        LOGGER.warn("User {} hat Beitrag {} in Gruppe {} gemeldet (Grund: {})", myId, postId, id, req.grund());
        return ResponseEntity.status(201).build();
    }

    // ── GET /gruppen/{id}/reports ──

    @GetMapping("/gruppen/{id}/reports")
    public ResponseEntity<List<BeitragMeldungDto>> getReports(@PathVariable("id") UUID id, Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId)
                .map(m -> m.getRolle() != GruppenRolle.ADMIN).orElse(true))
            return ResponseEntity.status(403).build();

        List<UUID> beitragIds = beitragRepository.findByGruppeIdOrderByCreatedAtDesc(id)
                .stream().map(GruppenbeitragEntity::getBeitragId).toList();

        List<BeitragMeldungDto> dtos = meldungRepository.findByBeitragIdIn(beitragIds)
                .stream()
                .map(m -> {
                    UserEntity melder = userRepository.findById(m.getMelderId()).orElse(null);
                    return new BeitragMeldungDto(m.getMeldungId(), m.getBeitragId(), m.getMelderId(),
                            melder != null ? melder.getName() : "Unbekannt",
                            m.getGrund(), m.getGemeldetAt());
                })
                .toList();
        return ResponseEntity.ok(dtos);
    }

    // ── DELETE /gruppen/{id}/reports/{meldungId} ──

    @DeleteMapping("/gruppen/{id}/reports/{meldungId}")
    public ResponseEntity<Void> dismissReport(@PathVariable("id") UUID id,
                                               @PathVariable("meldungId") UUID meldungId,
                                               Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId)
                .map(m -> m.getRolle() != GruppenRolle.ADMIN).orElse(true))
            return ResponseEntity.status(403).build();

        meldungRepository.deleteById(meldungId);
        LOGGER.debug("Admin {} hat Meldung {} in Gruppe {} abgewiesen", myId, meldungId, id);
        return ResponseEntity.noContent().build();
    }

    // ── Helpers ──

    private UUID resolveMyId(Principal principal) {
        if (principal == null) return null;
        return userService.requireUser(principal).getUserId();
    }

    private void deleteBeitragCascade(GruppenbeitragEntity beitrag) {
        UUID bid = beitrag.getBeitragId();
        meldungRepository.deleteByBeitragId(bid);
        stimmeRepository.deleteByBeitragId(bid);
        optionRepository.deleteByBeitragId(bid);
        likeRepository.deleteByBeitragId(bid);
        var kommentare = kommentarRepository
                .findByTargetTypeAndTargetIdOrderByCreatedAtAsc("GROUP_POST", bid);
        kommentarRepository.deleteAll(kommentare);
        beitragRepository.delete(beitrag);
    }

    private GruppenbeitragDto toDto(GruppenbeitragEntity b, UUID myId) {
        UserEntity author = userRepository.findById(b.getAuthorId()).orElse(null);
        long likeCount = likeRepository.countByBeitragId(b.getBeitragId());
        boolean likedByMe = likeRepository.findByBeitragIdAndUserId(b.getBeitragId(), myId).isPresent();
        long kommentarCount = kommentarRepository
                .countByTargetTypeAndTargetId("GROUP_POST", b.getBeitragId());
        boolean reported = meldungRepository.findByBeitragIdAndMelderId(b.getBeitragId(), myId).isPresent();

        List<UmfrageOptionDto> optionen = List.of();
        List<UUID> myVoteOptionIds = List.of();
        if (b.getBeitragTyp() == BeitragTyp.UMFRAGE) {
            optionen = optionRepository.findByBeitragIdOrderByReihenfolge(b.getBeitragId())
                    .stream()
                    .map(o -> new UmfrageOptionDto(o.getOptionId(), o.getText(), o.getReihenfolge(),
                            stimmeRepository.countByOptionId(o.getOptionId())))
                    .toList();
            myVoteOptionIds = stimmeRepository.findByBeitragIdAndUserId(b.getBeitragId(), myId)
                    .stream()
                    .map(UmfrageStimmeEntity::getOptionId)
                    .toList();
        }

        return new GruppenbeitragDto(
                b.getBeitragId(), b.getGruppeId(), b.getAuthorId(),
                author != null ? author.getName() : "Unbekannt",
                author != null ? author.getProfilePicture() : null,
                b.getBeitragTyp().name(), b.getText(), b.getMultiChoice(), b.getBilder(), b.getCreatedAt(),
                likeCount, likedByMe, kommentarCount, optionen, myVoteOptionIds, reported);
    }
}
