package de.oaa.xxx.feed;

import java.security.Principal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.http.ResponseEntity;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import de.oaa.xxx.feed.dto.FeedItemDto;
import de.oaa.xxx.feed.dto.FeedPostRequest;
import de.oaa.xxx.feed.entity.FeedPostEntity;
import de.oaa.xxx.feed.entity.FeedPostOptionEntity;
import de.oaa.xxx.feed.entity.FeedPostVoteEntity;
import de.oaa.xxx.feed.repository.FeedPostLikeRepository;
import de.oaa.xxx.feed.repository.FeedPostOptionRepository;
import de.oaa.xxx.feed.repository.FeedPostRepository;
import de.oaa.xxx.feed.repository.FeedPostVoteRepository;
import de.oaa.xxx.gruppe.BeitragTyp;
import de.oaa.xxx.gruppe.dto.UmfrageOptionDto;
import de.oaa.xxx.gruppe.entity.GruppenbeitragEntity;
import de.oaa.xxx.gruppe.entity.UmfrageStimmeEntity;
import de.oaa.xxx.gruppe.repository.GruppeRepository;
import de.oaa.xxx.gruppe.repository.GruppenbeitragLikeRepository;
import de.oaa.xxx.gruppe.repository.GruppenbeitragRepository;
import de.oaa.xxx.gruppe.repository.GruppenmitgliedRepository;
import de.oaa.xxx.gruppe.repository.UmfrageOptionRepository;
import de.oaa.xxx.gruppe.repository.UmfrageStimmeRepository;
import de.oaa.xxx.social.LikeService;
import de.oaa.xxx.social.entity.FriendshipEntity;
import de.oaa.xxx.social.repository.FriendshipRepository;
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;

@RestController
@RequestMapping("/feed")
public class FeedController {

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

    private final FeedPostRepository feedPostRepository;
    private final FeedPostLikeRepository feedPostLikeRepository;
    private final FeedPostOptionRepository feedPostOptionRepository;
    private final FeedPostVoteRepository feedPostVoteRepository;
    private final FriendshipRepository friendshipRepository;
    private final GruppenmitgliedRepository mitgliedRepository;
    private final GruppenbeitragRepository gruppenbeitragRepository;
    private final UmfrageOptionRepository umfrageOptionRepository;
    private final UmfrageStimmeRepository umfrageStimmeRepository;
    private final GruppenbeitragLikeRepository gruppenbeitragLikeRepository;
    private final GruppeRepository gruppeRepository;
    private final KommentarRepository kommentarRepository;
    private final UserRepository userRepository;
    private final UserService userService;
    private final LikeService likeService;

    public FeedController(FeedPostRepository feedPostRepository,
                          FeedPostLikeRepository feedPostLikeRepository,
                          FeedPostOptionRepository feedPostOptionRepository,
                          FeedPostVoteRepository feedPostVoteRepository,
                          FriendshipRepository friendshipRepository,
                          GruppenmitgliedRepository mitgliedRepository,
                          GruppenbeitragRepository gruppenbeitragRepository,
                          UmfrageOptionRepository umfrageOptionRepository,
                          UmfrageStimmeRepository umfrageStimmeRepository,
                          GruppenbeitragLikeRepository gruppenbeitragLikeRepository,
                          GruppeRepository gruppeRepository,
                          KommentarRepository kommentarRepository,
                          UserRepository userRepository,
                          UserService userService,
                          LikeService likeService) {
        this.feedPostRepository = feedPostRepository;
        this.feedPostLikeRepository = feedPostLikeRepository;
        this.feedPostOptionRepository = feedPostOptionRepository;
        this.feedPostVoteRepository = feedPostVoteRepository;
        this.friendshipRepository = friendshipRepository;
        this.mitgliedRepository = mitgliedRepository;
        this.gruppenbeitragRepository = gruppenbeitragRepository;
        this.umfrageOptionRepository = umfrageOptionRepository;
        this.umfrageStimmeRepository = umfrageStimmeRepository;
        this.gruppenbeitragLikeRepository = gruppenbeitragLikeRepository;
        this.gruppeRepository = gruppeRepository;
        this.kommentarRepository = kommentarRepository;
        this.userRepository = userRepository;
        this.userService = userService;
        this.likeService = likeService;
    }

    record FeedPage(List<FeedItemDto> posts, boolean hasMore) {}
    record VoteRequest(UUID optionId) {}

    // ── POST /feed/posts ──

    @PostMapping("/posts")
    public ResponseEntity<FeedItemDto> createPost(@RequestBody FeedPostRequest req, Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).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();
        }

        FeedPostEntity post = new FeedPostEntity();
        post.setPostId(UUID.randomUUID());
        post.setAuthorId(myId);
        post.setText(req.text().trim());
        post.setBeitragTyp(typ);
        post.setMultiChoice(typ == BeitragTyp.UMFRAGE ? req.multiChoice() : null);
        post.setBilder(req.bilder() != null ? req.bilder() : List.of());
        post.setPublic(req.isPublic());
        post.setCreatedAt(LocalDateTime.now());
        feedPostRepository.save(post);
        LOGGER.info("User {} hat Feed-Post {} erstellt (Typ: {}, public: {})", myId, post.getPostId(), typ, post.isPublic());

        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;
                FeedPostOptionEntity opt = new FeedPostOptionEntity();
                opt.setOptionId(UUID.randomUUID());
                opt.setPostId(post.getPostId());
                opt.setText(optText.trim());
                opt.setReihenfolge(i);
                feedPostOptionRepository.save(opt);
            }
        }

        return ResponseEntity.status(201).body(toFeedItemDtoFromPost(post, myId));
    }

    // ── GET /feed/mine ──

    @GetMapping("/mine")
    public ResponseEntity<FeedPage> getMyFeed(@RequestParam(defaultValue = "0") int page,
                                               @RequestParam(defaultValue = "10") int size,
                                               Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        // Collect friend IDs
        List<UUID> friendIds = friendshipRepository
                .findFriends(myId, FriendshipEntity.Status.ACCEPTED)
                .stream()
                .map(f -> f.getSenderId().equals(myId) ? f.getReceiverId() : f.getSenderId())
                .toList();

        List<UUID> authorIds = new ArrayList<>(friendIds);
        authorIds.add(myId);

        // Collect group IDs
        List<UUID> gruppeIds = mitgliedRepository.findByUserId(myId)
                .stream()
                .map(m -> m.getGruppeId())
                .toList();

        LocalDateTime since = LocalDateTime.now().minusDays(90);

        // Fetch feed posts from friends + self
        List<FeedPostEntity> feedPosts = feedPostRepository
                .findByAuthorIdInAndCreatedAtAfterOrderByCreatedAtDesc(authorIds, since);

        // Fetch gruppe posts
        List<GruppenbeitragEntity> gruppePosts = gruppeIds.isEmpty() ? List.of() :
                gruppenbeitragRepository.findByGruppeIdInAndCreatedAtAfterOrderByCreatedAtDesc(gruppeIds, since);

        // Merge, convert, sort
        List<FeedItemDto> merged = Stream.concat(
                feedPosts.stream().map(p -> toFeedItemDtoFromPost(p, myId)),
                gruppePosts.stream().map(b -> toFeedItemDtoFromGruppe(b, myId))
        ).sorted(Comparator.comparing(FeedItemDto::createdAt).reversed()).toList();

        int from = page * size;
        int to = Math.min(from + size, merged.size());
        List<FeedItemDto> items = from < merged.size() ? merged.subList(from, to) : List.of();
        boolean hasMore = to < merged.size();

        return ResponseEntity.ok(new FeedPage(items, hasMore));
    }

    // ── GET /feed/public ──

    @GetMapping("/public")
    public ResponseEntity<FeedPage> getPublicFeed(@RequestParam(defaultValue = "0") int page,
                                                   @RequestParam(defaultValue = "10") int size,
                                                   Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        Slice<FeedPostEntity> slice = feedPostRepository
                .findByIsPublicTrueOrderByCreatedAtDesc(PageRequest.of(page, size));

        List<FeedItemDto> items = slice.getContent().stream()
                .map(p -> toFeedItemDtoFromPost(p, myId))
                .toList();

        return ResponseEntity.ok(new FeedPage(items, slice.hasNext()));
    }

    // ── GET /feed/user/{userId} ──

    @GetMapping("/user/{userId}")
    public ResponseEntity<FeedPage> getUserPosts(@PathVariable UUID userId,
                                                  @RequestParam(defaultValue = "0") int page,
                                                  @RequestParam(defaultValue = "10") int size,
                                                  Principal principal) {
        UUID myId = resolveMyId(principal);
        if (myId == null) return ResponseEntity.status(401).build();

        PageRequest pageable = PageRequest.of(page, size);
        List<FeedPostEntity> posts;
        if (myId.equals(userId)) {
            posts = feedPostRepository.findByAuthorIdOrderByCreatedAtDesc(userId, pageable);
        } else {
            posts = feedPostRepository.findByAuthorIdAndIsPublicTrueOrderByCreatedAtDesc(userId, pageable);
        }

        // Check if there's a next page
        PageRequest nextPageable = PageRequest.of(page + 1, size);
        List<FeedPostEntity> nextPage;
        if (myId.equals(userId)) {
            nextPage = feedPostRepository.findByAuthorIdOrderByCreatedAtDesc(userId, nextPageable);
        } else {
            nextPage = feedPostRepository.findByAuthorIdAndIsPublicTrueOrderByCreatedAtDesc(userId, nextPageable);
        }

        List<FeedItemDto> items = posts.stream()
                .map(p -> toFeedItemDtoFromPost(p, myId))
                .toList();

        return ResponseEntity.ok(new FeedPage(items, !nextPage.isEmpty()));
    }

    // ── POST /feed/posts/{id}/like ──

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

        likeService.toggleFeedPostLike(id, myId);
        return ResponseEntity.ok().build();
    }

    // ── POST /feed/posts/{id}/vote ──

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

        var postOpt = feedPostRepository.findById(id);
        if (postOpt.isEmpty()) return ResponseEntity.notFound().build();
        FeedPostEntity post = postOpt.get();

        var optOpt = feedPostOptionRepository.findById(req.optionId());
        if (optOpt.isEmpty() || !optOpt.get().getPostId().equals(id))
            return ResponseEntity.badRequest().build();

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

        var existingVote = feedPostVoteRepository.findByOptionIdAndUserId(req.optionId(), myId);
        if (existingVote.isPresent()) {
            feedPostVoteRepository.delete(existingVote.get());
            return ResponseEntity.ok().build();
        }

        if (!isMultiChoice) {
            List<FeedPostVoteEntity> existing = feedPostVoteRepository.findByPostIdAndUserId(id, myId);
            feedPostVoteRepository.deleteAll(existing);
        }

        FeedPostVoteEntity vote = new FeedPostVoteEntity();
        vote.setStimmeId(UUID.randomUUID());
        vote.setOptionId(req.optionId());
        vote.setPostId(id);
        vote.setUserId(myId);
        feedPostVoteRepository.save(vote);
        LOGGER.debug("User {} hat für Option {} in Feed-Post {} gestimmt", myId, req.optionId(), id);
        return ResponseEntity.ok().build();
    }

    // ── DELETE /feed/posts/{id} ──

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

        var postOpt = feedPostRepository.findById(id);
        if (postOpt.isEmpty()) return ResponseEntity.notFound().build();
        FeedPostEntity post = postOpt.get();

        if (!post.getAuthorId().equals(myId)) return ResponseEntity.status(403).build();

        feedPostVoteRepository.deleteByPostId(id);
        feedPostOptionRepository.deleteByPostId(id);
        feedPostLikeRepository.deleteByPostId(id);
        var kommentare = kommentarRepository.findByTargetTypeAndTargetIdOrderByCreatedAtAsc("FEED_POST", id);
        kommentarRepository.deleteAll(kommentare);
        feedPostRepository.delete(post);
        LOGGER.info("User {} hat Feed-Post {} gelöscht", myId, id);

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

    // ── Helpers ──

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

    private FeedItemDto toFeedItemDtoFromPost(FeedPostEntity p, UUID myId) {
        UserEntity author = userRepository.findById(p.getAuthorId()).orElse(null);
        long likeCount = feedPostLikeRepository.countByPostId(p.getPostId());
        boolean likedByMe = feedPostLikeRepository.findByPostIdAndUserId(p.getPostId(), myId).isPresent();
        long kommentarCount = kommentarRepository.countByTargetTypeAndTargetId("FEED_POST", p.getPostId());

        List<UmfrageOptionDto> optionen = List.of();
        List<UUID> myVoteOptionIds = List.of();
        if (p.getBeitragTyp() == BeitragTyp.UMFRAGE) {
            optionen = feedPostOptionRepository.findByPostIdOrderByReihenfolge(p.getPostId())
                    .stream()
                    .map(o -> new UmfrageOptionDto(o.getOptionId(), o.getText(), o.getReihenfolge(),
                            feedPostVoteRepository.countByOptionId(o.getOptionId())))
                    .toList();
            myVoteOptionIds = feedPostVoteRepository.findByPostIdAndUserId(p.getPostId(), myId)
                    .stream()
                    .map(FeedPostVoteEntity::getOptionId)
                    .toList();
        }

        return new FeedItemDto(
                p.getPostId(), "FEED",
                null, null,
                p.getAuthorId(),
                author != null ? author.getName() : "Unbekannt",
                author != null ? author.getProfilePicture() : null,
                p.getBeitragTyp().name(), p.getText(), p.getMultiChoice(), p.getBilder(),
                p.getCreatedAt(),
                likeCount, likedByMe, kommentarCount,
                optionen, myVoteOptionIds,
                p.isPublic()
        );
    }

    private FeedItemDto toFeedItemDtoFromGruppe(GruppenbeitragEntity b, UUID myId) {
        UserEntity author = userRepository.findById(b.getAuthorId()).orElse(null);
        long likeCount = gruppenbeitragLikeRepository.countByBeitragId(b.getBeitragId());
        boolean likedByMe = gruppenbeitragLikeRepository.findByBeitragIdAndUserId(b.getBeitragId(), myId).isPresent();
        long kommentarCount = kommentarRepository.countByTargetTypeAndTargetId("GROUP_POST", b.getBeitragId());
        String gruppeName = gruppeRepository.findById(b.getGruppeId())
                .map(g -> g.getName())
                .orElse("Gruppe");

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

        return new FeedItemDto(
                b.getBeitragId(), "GROUP",
                b.getGruppeId(), gruppeName,
                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,
                false
        );
    }
}
