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.entity.KommentarEntity;
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.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/gruppen")
public class GruppeController {

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

    private final GruppeRepository gruppeRepository;
    private final GruppenmitgliedRepository mitgliedRepository;
    private final BeitrittsanfrageRepository anfrageRepository;
    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;

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

    record CreateGruppeRequest(String name, String beschreibung, String bild, boolean isPrivate) {}
    record JoinRequest(String nachricht) {}
    record UpdateGruppeRequest(String name, String beschreibung, String bild, Boolean isPrivate) {}

    // ── GET /gruppe/search?q= ──

    @GetMapping("/search")
    public ResponseEntity<List<GruppeDto>> search(@RequestParam String q, Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        List<GruppeEntity> gruppen = gruppeRepository.findByNameContainingIgnoreCase(q)
                .stream().limit(30).toList();
        List<UUID> gruppeIds = gruppen.stream().map(GruppeEntity::getGruppeId).toList();
        Map<UUID, LocalDateTime> latestActivity = buildLatestActivityMap(gruppeIds);
        List<GruppeDto> result = gruppen.stream()
                .map(g -> toDto(g, myId))
                .sorted((a, b) -> {
                    LocalDateTime la = latestActivity.getOrDefault(a.gruppeId(), a.createdAt());
                    LocalDateTime lb = latestActivity.getOrDefault(b.gruppeId(), b.createdAt());
                    return lb.compareTo(la);
                })
                .toList();
        return ResponseEntity.ok(result);
    }

    // ── GET /gruppe/mine ──

    @GetMapping("/mine")
    public ResponseEntity<List<GruppeDto>> mine(Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        List<GruppeEntity> gruppen = mitgliedRepository.findByUserId(myId)
                .stream()
                .map(m -> gruppeRepository.findById(m.getGruppeId()).orElse(null))
                .filter(Objects::nonNull)
                .toList();
        List<UUID> gruppeIds = gruppen.stream().map(GruppeEntity::getGruppeId).toList();
        Map<UUID, LocalDateTime> latestActivity = buildLatestActivityMap(gruppeIds);
        List<GruppeDto> result = gruppen.stream()
                .map(g -> toDto(g, myId))
                .sorted((a, b) -> {
                    LocalDateTime la = latestActivity.getOrDefault(a.gruppeId(), a.createdAt());
                    LocalDateTime lb = latestActivity.getOrDefault(b.gruppeId(), b.createdAt());
                    return lb.compareTo(la);
                })
                .toList();
        return ResponseEntity.ok(result);
    }

    // ── GET /gruppe/{id} ──

    @GetMapping("/{id}")
    public ResponseEntity<GruppeDto> getGruppe(@PathVariable UUID id, Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        return gruppeRepository.findById(id)
                .map(g -> ResponseEntity.ok(toDto(g, myId)))
                .orElse(ResponseEntity.notFound().build());
    }

    // ── POST /gruppe ──

    @PostMapping
    public ResponseEntity<GruppeDto> createGruppe(@RequestBody CreateGruppeRequest req, Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (req.name() == null || req.name().isBlank()) return ResponseEntity.badRequest().build();

        GruppeEntity gruppe = new GruppeEntity();
        gruppe.setGruppeId(UUID.randomUUID());
        gruppe.setName(req.name().trim());
        gruppe.setBeschreibung(req.beschreibung());
        gruppe.setBild(req.bild());
        gruppe.setPrivate(req.isPrivate());
        gruppe.setCreatedAt(LocalDateTime.now());
        gruppe.setCreatedByUserId(myId);
        gruppeRepository.save(gruppe);

        GruppenmitgliedEntity admin = new GruppenmitgliedEntity();
        admin.setMitgliedId(UUID.randomUUID());
        admin.setGruppeId(gruppe.getGruppeId());
        admin.setUserId(myId);
        admin.setRolle(GruppenRolle.ADMIN);
        admin.setJoinedAt(LocalDateTime.now());
        mitgliedRepository.save(admin);
        LOGGER.info("User {} hat Gruppe '{}' ({}) erstellt", myId, gruppe.getName(), gruppe.getGruppeId());

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

    // ── PUT /gruppe/{id} ──

    @PutMapping("/{id}")
    public ResponseEntity<GruppeDto> updateGruppe(@PathVariable UUID id,
                                                   @RequestBody UpdateGruppeRequest req,
                                                   Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        var gruppeOpt = gruppeRepository.findById(id);
        if (gruppeOpt.isEmpty()) return ResponseEntity.notFound().build();
        GruppeEntity gruppe = gruppeOpt.get();

        if (!isAdmin(id, myId)) return ResponseEntity.status(403).build();

        if (req.name() != null && !req.name().isBlank()) gruppe.setName(req.name().trim());
        if (req.beschreibung() != null) gruppe.setBeschreibung(req.beschreibung());
        if (req.bild() != null) gruppe.setBild(req.bild());
        if (req.isPrivate() != null) gruppe.setPrivate(req.isPrivate());
        gruppeRepository.save(gruppe);
        LOGGER.debug("User {} hat Gruppe {} aktualisiert", myId, id);

        return ResponseEntity.ok(toDto(gruppe, myId));
    }

    // ── DELETE /gruppe/{id} ──

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

        var gruppeOpt = gruppeRepository.findById(id);
        if (gruppeOpt.isEmpty()) return ResponseEntity.notFound().build();
        if (!isAdmin(id, myId)) return ResponseEntity.status(403).build();

        // Cascade delete
        List<GruppenbeitragEntity> beitraege = beitragRepository.findByGruppeIdOrderByCreatedAtDesc(id);
        for (GruppenbeitragEntity b : beitraege) {
            UUID bid = b.getBeitragId();
            meldungRepository.deleteByBeitragId(bid);
            stimmeRepository.deleteByBeitragId(bid);
            optionRepository.deleteByBeitragId(bid);
            likeRepository.deleteByBeitragId(bid);
            // Kommentare on GROUP_POST
            List<KommentarEntity> kommentare = kommentarRepository
                    .findByTargetTypeAndTargetIdOrderByCreatedAtAsc("GROUP_POST", bid);
            for (KommentarEntity k : kommentare) {
                kommentarRepository.delete(k);
            }
        }
        beitragRepository.deleteByGruppeId(id);
        anfrageRepository.deleteByGruppeId(id);
        mitgliedRepository.deleteByGruppeId(id);
        gruppeRepository.deleteById(id);
        LOGGER.info("User {} hat Gruppe {} gelöscht", myId, id);

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

    // ── POST /gruppe/{id}/join ──

    @PostMapping("/{id}/join")
    public ResponseEntity<Void> join(@PathVariable UUID id,
                                     @RequestBody(required = false) JoinRequest req,
                                     Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        var gruppeOpt = gruppeRepository.findById(id);
        if (gruppeOpt.isEmpty()) return ResponseEntity.notFound().build();
        GruppeEntity gruppe = gruppeOpt.get();

        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, myId).isPresent())
            return ResponseEntity.status(409).build();

        if (gruppe.isPrivate()) {
            // Check no pending request already
            var existingReq = anfrageRepository.findByGruppeIdAndUserId(id, myId);
            if (existingReq.isPresent() && existingReq.get().getStatus() == AnfrageStatus.AUSSTEHEND)
                return ResponseEntity.status(409).build();

            BeitrittsanfrageEntity anfrage = new BeitrittsanfrageEntity();
            anfrage.setAnfrageId(UUID.randomUUID());
            anfrage.setGruppeId(id);
            anfrage.setUserId(myId);
            anfrage.setNachricht(req != null ? req.nachricht() : null);
            anfrage.setAngefragtAt(LocalDateTime.now());
            anfrage.setStatus(AnfrageStatus.AUSSTEHEND);
            anfrageRepository.save(anfrage);
            LOGGER.info("User {} hat Beitrittsanfrage für private Gruppe {} gestellt", myId, id);
            return ResponseEntity.status(201).build();
        } else {
            GruppenmitgliedEntity mitglied = new GruppenmitgliedEntity();
            mitglied.setMitgliedId(UUID.randomUUID());
            mitglied.setGruppeId(id);
            mitglied.setUserId(myId);
            mitglied.setRolle(GruppenRolle.MITGLIED);
            mitglied.setJoinedAt(LocalDateTime.now());
            mitgliedRepository.save(mitglied);
            LOGGER.info("User {} ist Gruppe {} beigetreten", myId, id);
            return ResponseEntity.status(201).build();
        }
    }

    // ── DELETE /gruppe/{id}/leave ──

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

        mitgliedRepository.deleteByGruppeIdAndUserId(id, myId);
        LOGGER.info("User {} hat Gruppe {} verlassen", myId, id);
        return ResponseEntity.noContent().build();
    }

    // ── GET /gruppe/{id}/members ──

    @GetMapping("/{id}/members")
    public ResponseEntity<List<Map<String, Object>>> getMembers(@PathVariable UUID id, 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();

        List<Map<String, Object>> result = mitgliedRepository.findByGruppeId(id).stream()
                .map(m -> {
                    Map<String, Object> map = new LinkedHashMap<>();
                    map.put("mitgliedId", m.getMitgliedId());
                    map.put("userId", m.getUserId());
                    map.put("rolle", m.getRolle().name());
                    map.put("joinedAt", m.getJoinedAt());
                    userRepository.findById(m.getUserId()).ifPresent(u -> {
                        map.put("userName", u.getName());
                        map.put("userPicture", u.getProfilePicture());
                    });
                    return map;
                })
                .toList();
        return ResponseEntity.ok(result);
    }

    // ── DELETE /gruppe/{id}/members/{userId} ──

    @DeleteMapping("/{id}/members/{userId}")
    public ResponseEntity<Void> removeMember(@PathVariable UUID id,
                                              @PathVariable UUID userId,
                                              Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (!isAdmin(id, myId)) return ResponseEntity.status(403).build();

        mitgliedRepository.deleteByGruppeIdAndUserId(id, userId);
        LOGGER.warn("Admin {} hat User {} aus Gruppe {} entfernt", myId, userId, id);
        return ResponseEntity.noContent().build();
    }

    // ── POST /gruppe/{id}/members/{userId}/promote ──

    @PostMapping("/{id}/members/{userId}/promote")
    public ResponseEntity<Void> promote(@PathVariable UUID id,
                                         @PathVariable UUID userId,
                                         Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (!isAdmin(id, myId)) return ResponseEntity.status(403).build();

        var m = mitgliedRepository.findFirstByGruppeIdAndUserId(id, userId);
        if (m.isEmpty()) return ResponseEntity.notFound().build();
        m.get().setRolle(GruppenRolle.ADMIN);
        mitgliedRepository.save(m.get());
        LOGGER.info("Admin {} hat User {} in Gruppe {} zum Admin befördert", myId, userId, id);
        return ResponseEntity.ok().build();
    }

    // ── GET /gruppe/{id}/requests ──

    @GetMapping("/{id}/requests")
    public ResponseEntity<List<BeitrittsanfrageDto>> getRequests(@PathVariable UUID id, Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (!isAdmin(id, myId)) return ResponseEntity.status(403).build();

        List<BeitrittsanfrageDto> dtos = anfrageRepository
                .findByGruppeIdAndStatus(id, AnfrageStatus.AUSSTEHEND)
                .stream()
                .map(a -> {
                    UserEntity u = userRepository.findById(a.getUserId()).orElse(null);
                    return new BeitrittsanfrageDto(a.getAnfrageId(), a.getGruppeId(), a.getUserId(),
                            u != null ? u.getName() : "Unbekannt",
                            u != null ? u.getProfilePicture() : null,
                            a.getNachricht(), a.getAngefragtAt());
                })
                .toList();
        return ResponseEntity.ok(dtos);
    }

    // ── POST /gruppe/{id}/requests/{reqId}/approve ──

    @PostMapping("/{id}/requests/{reqId}/approve")
    public ResponseEntity<Void> approveRequest(@PathVariable UUID id,
                                                @PathVariable UUID reqId,
                                                Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (!isAdmin(id, myId)) return ResponseEntity.status(403).build();

        var anfOpt = anfrageRepository.findById(reqId);
        if (anfOpt.isEmpty()) return ResponseEntity.notFound().build();
        BeitrittsanfrageEntity anfrage = anfOpt.get();
        anfrage.setStatus(AnfrageStatus.GENEHMIGT);
        anfrageRepository.save(anfrage);
        LOGGER.info("Admin {} hat Beitrittsanfrage {} (User: {}) für Gruppe {} genehmigt", myId, reqId, anfrage.getUserId(), id);

        if (mitgliedRepository.findFirstByGruppeIdAndUserId(id, anfrage.getUserId()).isEmpty()) {
            GruppenmitgliedEntity mitglied = new GruppenmitgliedEntity();
            mitglied.setMitgliedId(UUID.randomUUID());
            mitglied.setGruppeId(id);
            mitglied.setUserId(anfrage.getUserId());
            mitglied.setRolle(GruppenRolle.MITGLIED);
            mitglied.setJoinedAt(LocalDateTime.now());
            mitgliedRepository.save(mitglied);
        }

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

    // ── DELETE /gruppe/{id}/requests/{reqId} ──

    @DeleteMapping("/{id}/requests/{reqId}")
    public ResponseEntity<Void> rejectRequest(@PathVariable UUID id,
                                               @PathVariable UUID reqId,
                                               Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();
        if (!isAdmin(id, myId)) return ResponseEntity.status(403).build();

        var anfOpt = anfrageRepository.findById(reqId);
        if (anfOpt.isEmpty()) return ResponseEntity.notFound().build();
        BeitrittsanfrageEntity anfrage = anfOpt.get();
        anfrage.setStatus(AnfrageStatus.ABGELEHNT);
        anfrageRepository.save(anfrage);
        LOGGER.debug("Admin {} hat Beitrittsanfrage {} für Gruppe {} abgelehnt", myId, reqId, id);
        return ResponseEntity.noContent().build();
    }

    // ── GET /gruppen/reports/pending/count ──

    @GetMapping("/reports/pending/count")
    public ResponseEntity<Long> pendingReportsCount(Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        long count = mitgliedRepository.findByUserId(myId).stream()
                .filter(m -> m.getRolle() == GruppenRolle.ADMIN)
                .mapToLong(m -> {
                    List<GruppenbeitragEntity> beitraege = beitragRepository.findByGruppeIdOrderByCreatedAtDesc(m.getGruppeId());
                    List<UUID> ids = beitraege.stream().map(GruppenbeitragEntity::getBeitragId).toList();
                    return ids.isEmpty() ? 0L : meldungRepository.findByBeitragIdIn(ids).size();
                })
                .sum();
        return ResponseEntity.ok(count);
    }

    // ── GET /gruppen/requests/pending/count ──

    @GetMapping("/requests/pending/count")
    public ResponseEntity<Long> pendingRequestCount(Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        long count = mitgliedRepository.findByUserId(myId).stream()
                .filter(m -> m.getRolle() == GruppenRolle.ADMIN)
                .mapToLong(m -> anfrageRepository.findByGruppeIdAndStatus(m.getGruppeId(), AnfrageStatus.AUSSTEHEND).size())
                .sum();
        return ResponseEntity.ok(count);
    }

    // ── GET /gruppe/requests/mine ──

    @GetMapping("/requests/mine")
    public ResponseEntity<List<Map<String, Object>>> myRequests(Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        List<Map<String, Object>> result = anfrageRepository.findByUserIdAndStatus(myId, AnfrageStatus.AUSSTEHEND)
                .stream()
                .map(a -> {
                    Map<String, Object> map = new LinkedHashMap<>();
                    map.put("anfrageId", a.getAnfrageId());
                    map.put("gruppeId", a.getGruppeId());
                    map.put("nachricht", a.getNachricht());
                    map.put("angefragtAt", a.getAngefragtAt());
                    gruppeRepository.findById(a.getGruppeId()).ifPresent(g -> map.put("gruppeName", g.getName()));
                    return map;
                })
                .toList();
        return ResponseEntity.ok(result);
    }

    // ── DELETE /gruppe/{id}/requests/mine (withdraw own request) ──

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

        anfrageRepository.findByGruppeIdAndUserId(id, myId).ifPresent(a -> {
            if (a.getStatus() == AnfrageStatus.AUSSTEHEND) {
                anfrageRepository.delete(a);
                LOGGER.debug("User {} hat eigene Beitrittsanfrage für Gruppe {} zurückgezogen", myId, id);
            }
        });
        return ResponseEntity.noContent().build();
    }

    // ── Helpers ──

    private Map<UUID, LocalDateTime> buildLatestActivityMap(List<UUID> gruppeIds) {
        if (gruppeIds.isEmpty()) return Map.of();
        Map<UUID, LocalDateTime> map = new HashMap<>();
        beitragRepository.findLatestCreatedAtByGruppeIds(gruppeIds).forEach(row -> {
            UUID gId = (UUID) row[0];
            LocalDateTime latest = (LocalDateTime) row[1];
            map.put(gId, latest);
        });
        return map;
    }

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

    private boolean isAdmin(UUID gruppeId, UUID userId) {
        return mitgliedRepository.findFirstByGruppeIdAndUserId(gruppeId, userId)
                .map(m -> m.getRolle() == GruppenRolle.ADMIN)
                .orElse(false);
    }

    GruppeDto toDto(GruppeEntity g, UUID myId) {
        long memberCount = mitgliedRepository.countByGruppeId(g.getGruppeId());
        long postCount = beitragRepository.findByGruppeIdOrderByCreatedAtDesc(g.getGruppeId()).size();
        String myRole = mitgliedRepository.findFirstByGruppeIdAndUserId(g.getGruppeId(), myId)
                .map(m -> m.getRolle().name())
                .orElse(null);
        String myRequestStatus = null;
        if (myRole == null) {
            myRequestStatus = anfrageRepository.findByGruppeIdAndUserId(g.getGruppeId(), myId)
                    .filter(a -> a.getStatus() == AnfrageStatus.AUSSTEHEND)
                    .map(a -> a.getStatus().name())
                    .orElse(null);
        }
        return new GruppeDto(g.getGruppeId(), g.getName(), g.getBeschreibung(), g.getBild(),
                g.isPrivate(), g.getCreatedAt(), memberCount, postCount, myRole, myRequestStatus);
    }
}
