package de.oaa.xxx.social;

import de.oaa.xxx.social.dto.ConversationSummary;
import de.oaa.xxx.social.dto.FriendshipDto;
import de.oaa.xxx.social.dto.MessageDto;
import de.oaa.xxx.social.dto.UserProfile;
import de.oaa.xxx.social.entity.FriendshipEntity;
import de.oaa.xxx.social.entity.FriendshipEntity.Status;
import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.social.entity.MessageEntity;
import de.oaa.xxx.social.repository.FriendshipRepository;
import de.oaa.xxx.social.repository.MessageRepository;
import de.oaa.xxx.support.SupportUserService;
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.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/social")
public class SocialController {

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

    private final UserRepository userRepository;
    private final FriendshipRepository friendshipRepository;
    private final MessageRepository messageRepository;
    private final SseService sseService;
    private final SystemMessageService systemMessageService;
    private final UserService userService;

    public SocialController(UserRepository userRepository,
                            FriendshipRepository friendshipRepository,
                            MessageRepository messageRepository,
                            SseService sseService,
                            SystemMessageService systemMessageService,
                            UserService userService) {
        this.userRepository = userRepository;
        this.friendshipRepository = friendshipRepository;
        this.messageRepository = messageRepository;
        this.sseService = sseService;
        this.systemMessageService = systemMessageService;
        this.userService = userService;
    }

    record FriendRequestBody(UUID receiverId) {}
    record FriendshipActionBody(UUID friendshipId) {}
    record SendMessageBody(UUID receiverId, String text) {}

    // ── User Profile ──

    @GetMapping("/users/{userId}")
    public ResponseEntity<UserProfile> getUserProfile(@PathVariable UUID userId, Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();
        return userRepository.findById(userId)
                .map(u -> ResponseEntity.ok(toUserProfileWithStatus(u, myId)))
                .orElse(ResponseEntity.notFound().build());
    }

    // ── User Search ──

    @GetMapping("/users/search")
    public ResponseEntity<List<UserProfile>> searchUsers(@RequestParam String q, Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();

        List<UserEntity> results = userRepository.findByNameContainingIgnoreCase(q);
        List<UserProfile> profiles = results.stream()
                .filter(u -> !u.getUserId().equals(myId))
                .limit(20)
                .map(u -> toUserProfileWithStatus(u, myId))
                .toList();
        return ResponseEntity.ok(profiles);
    }

    // ── Friendship ──

    @PostMapping("/friends/request")
    public ResponseEntity<Void> sendFriendRequest(@RequestBody FriendRequestBody body, Principal principal) {
        var me = userService.requireUser(principal);
        UUID myId = me.getUserId();

        if (myId.equals(body.receiverId())) {
            return ResponseEntity.badRequest().build();
        }
        if (friendshipRepository.findExisting(myId, body.receiverId()).isPresent()) {
            return ResponseEntity.status(409).build();
        }
        FriendshipEntity f = new FriendshipEntity();
        f.setFriendshipId(UUID.randomUUID());
        f.setSenderId(myId);
        f.setReceiverId(body.receiverId());
        f.setStatus(Status.PENDING);
        f.setCreatedAt(LocalDateTime.now());
        friendshipRepository.save(f);
        LOGGER.info("User {} hat Freundschaftsanfrage an User {} gesendet", myId, body.receiverId());

        String senderName = me.getName();
        systemMessageService.send(myId, body.receiverId(),
                senderName + " hat dir eine Freundschaftsanfrage gesendet.",
                "/community/benutzer.html?userId=" + myId,
                MessageCause.FRIENDREQUEST);

        return ResponseEntity.status(201).build();
    }

    @PostMapping("/friends/accept")
    public ResponseEntity<Void> acceptFriendRequest(@RequestBody FriendshipActionBody body, Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();

        var fOpt = friendshipRepository.findById(body.friendshipId());
        if (fOpt.isEmpty()) return ResponseEntity.notFound().build();
        FriendshipEntity f = fOpt.get();
        if (!f.getReceiverId().equals(myId)) return ResponseEntity.status(403).build();

        f.setStatus(Status.ACCEPTED);
        friendshipRepository.save(f);
        LOGGER.info("User {} hat Freundschaftsanfrage {} angenommen", myId, body.friendshipId());
        return ResponseEntity.ok().build();
    }

    @DeleteMapping("/friends/reject")
    public ResponseEntity<Void> rejectOrRemoveFriend(@RequestBody FriendshipActionBody body, Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();

        var fOpt = friendshipRepository.findById(body.friendshipId());
        if (fOpt.isEmpty()) return ResponseEntity.notFound().build();
        FriendshipEntity f = fOpt.get();
        if (!f.getSenderId().equals(myId) && !f.getReceiverId().equals(myId)) {
            return ResponseEntity.status(403).build();
        }
        friendshipRepository.delete(f);
        LOGGER.info("User {} hat Freundschaft/Anfrage {} gelöscht", myId, body.friendshipId());
        return ResponseEntity.noContent().build();
    }

    @GetMapping("/friends/user/{userId}")
    public ResponseEntity<List<UserProfile>> getFriendsOfUser(@PathVariable UUID userId, Principal principal) {
        userService.requireUser(principal);
        List<UserProfile> profiles = friendshipRepository.findFriends(userId, Status.ACCEPTED).stream()
                .map(f -> {
                    UUID friendId = f.getSenderId().equals(userId) ? f.getReceiverId() : f.getSenderId();
                    return userRepository.findById(friendId)
                            .map(u -> new UserProfile(u.getUserId(), u.getName(), u.getProfilePicture(), u.getProfilePictureHq(), null))
                            .orElse(null);
                })
                .filter(Objects::nonNull)
                .toList();
        return ResponseEntity.ok(profiles);
    }

    @GetMapping("/friends")
    public ResponseEntity<List<FriendshipDto>> getFriends(Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();

        List<FriendshipDto> dtos = friendshipRepository.findFriends(myId, Status.ACCEPTED).stream()
                .map(f -> {
                    UUID friendId = f.getSenderId().equals(myId) ? f.getReceiverId() : f.getSenderId();
                    return userRepository.findById(friendId)
                            .map(u -> new FriendshipDto(
                                    f.getFriendshipId(),
                                    new UserProfile(u.getUserId(), u.getName(), u.getProfilePicture(), u.getProfilePictureHq(), "FRIEND"),
                                    f.getStatus().name(),
                                    f.getCreatedAt()))
                            .orElse(null);
                })
                .filter(Objects::nonNull)
                .toList();
        return ResponseEntity.ok(dtos);
    }

    @GetMapping("/friends/pending")
    public ResponseEntity<List<FriendshipDto>> getPendingRequests(Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();

        List<FriendshipDto> dtos = friendshipRepository.findByReceiverIdAndStatus(myId, Status.PENDING).stream()
                .map(f -> userRepository.findById(f.getSenderId())
                        .map(u -> new FriendshipDto(
                                f.getFriendshipId(),
                                new UserProfile(u.getUserId(), u.getName(), u.getProfilePicture(), u.getProfilePictureHq(), "PENDING_RECEIVED"),
                                f.getStatus().name(),
                                f.getCreatedAt()))
                        .orElse(null))
                .filter(Objects::nonNull)
                .toList();
        return ResponseEntity.ok(dtos);
    }

    @GetMapping("/friends/pending/count")
    public ResponseEntity<Long> getPendingCount(Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();
        return ResponseEntity.ok(friendshipRepository.countByReceiverIdAndStatus(myId, Status.PENDING));
    }

    // ── Messages ──

    @PostMapping("/messages")
    public ResponseEntity<Void> sendMessage(@RequestBody SendMessageBody body, Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();

        if (body.text() == null || body.text().isBlank()) return ResponseEntity.badRequest().build();

        // Nachrichten an den Support-Account sind nicht erlaubt
        if (SupportUserService.SUPPORT_USER_ID.equals(body.receiverId())) {
            return ResponseEntity.status(403).build();
        }

        MessageEntity msg = new MessageEntity();
        msg.setMessageId(UUID.randomUUID());
        msg.setSenderId(myId);
        msg.setReceiverId(body.receiverId());
        msg.setText(body.text().trim());
        msg.setSentAt(LocalDateTime.now());
        messageRepository.save(msg);
        LOGGER.debug("User {} hat Nachricht an User {} gesendet", myId, body.receiverId());
        long unread = messageRepository.countUnread(body.receiverId());
        sseService.push(body.receiverId(), "DM", Map.of("unreadCount", unread, "senderId", myId.toString()));
        return ResponseEntity.status(201).build();
    }

    @GetMapping("/messages")
    public ResponseEntity<List<ConversationSummary>> getConversations(Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();

        List<MessageEntity> allMessages = messageRepository.findAllByUser(myId);

        // Group by partner, keep most recent message per partner
        Map<UUID, MessageEntity> latestByPartner = new LinkedHashMap<>();
        for (MessageEntity m : allMessages) {
            UUID partnerId = m.getSenderId().equals(myId) ? m.getReceiverId() : m.getSenderId();
            latestByPartner.putIfAbsent(partnerId, m);
        }

        List<ConversationSummary> summaries = new ArrayList<>();
        for (Map.Entry<UUID, MessageEntity> entry : latestByPartner.entrySet()) {
            UUID partnerId = entry.getKey();
            MessageEntity lastMsg = entry.getValue();
            var partnerOpt = userRepository.findById(partnerId);
            if (partnerOpt.isEmpty()) continue;

            UserProfile partnerProfile = toUserProfileWithStatus(partnerOpt.get(), myId);
            MessageDto lastMsgDto = toMessageDto(lastMsg);
            long unreadCount = allMessages.stream()
                    .filter(m -> m.getSenderId().equals(partnerId)
                            && m.getReceiverId().equals(myId)
                            && m.getReadAt() == null)
                    .count();
            summaries.add(new ConversationSummary(partnerProfile, lastMsgDto, unreadCount));
        }
        return ResponseEntity.ok(summaries);
    }

    @GetMapping("/messages/unread/count")
    public ResponseEntity<Long> getUnreadCount(Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();
        return ResponseEntity.ok(messageRepository.countUnread(myId));
    }

    private static final int MSG_PAGE_SIZE = 20;

    @GetMapping("/messages/{partnerId}")
    public ResponseEntity<?> getConversation(
            @PathVariable UUID partnerId,
            @RequestParam(required = false) String before,
            @RequestParam(required = false) String after,
            Principal principal) {
        UUID myId = userService.requireUser(principal).getUserId();

        if (after != null) {
            LocalDateTime afterDt = LocalDateTime.parse(after);
            List<MessageEntity> newMsgs = messageRepository.findConversationAfter(myId, partnerId, afterDt);
            messageRepository.markAsRead(myId, partnerId, LocalDateTime.now());
            return ResponseEntity.ok(Map.of("messages", newMsgs.stream().map(this::toMessageDto).toList(), "hasMore", false));
        }

        List<MessageEntity> messages;
        if (before != null) {
            LocalDateTime beforeDt = LocalDateTime.parse(before);
            messages = new ArrayList<>(messageRepository.findConversationBefore(myId, partnerId, beforeDt, PageRequest.of(0, MSG_PAGE_SIZE + 1)));
        } else {
            messages = new ArrayList<>(messageRepository.findConversation(myId, partnerId, PageRequest.of(0, MSG_PAGE_SIZE + 1)));
            messageRepository.markAsRead(myId, partnerId, LocalDateTime.now());
        }

        boolean hasMore = messages.size() > MSG_PAGE_SIZE;
        if (hasMore) messages = messages.subList(0, MSG_PAGE_SIZE);

        // DESC order from DB → reverse to oldest-first for client
        Collections.reverse(messages);
        return ResponseEntity.ok(Map.of("messages", messages.stream().map(this::toMessageDto).toList(), "hasMore", hasMore));
    }

    // ── Helpers ──

    private UserProfile toUserProfileWithStatus(UserEntity user, UUID myId) {
        boolean isOwn = user.getUserId().equals(myId);
        String status = "NONE";
        if (!isOwn) {
            var existing = friendshipRepository.findExisting(myId, user.getUserId());
            if (existing.isPresent()) {
                FriendshipEntity f = existing.get();
                if (f.getStatus() == Status.ACCEPTED) {
                    status = "FRIEND";
                } else if (f.getSenderId().equals(myId)) {
                    status = "PENDING_SENT";
                } else {
                    status = "PENDING_RECEIVED";
                }
            }
        }
        boolean isFriend = isOwn || "FRIEND".equals(status);

        // Grunddaten nur zurückgeben wenn berechtigt
        de.oaa.xxx.user.Sichtbarkeit svGd = user.getSichtbarkeitGrunddaten();
        boolean showGrunddaten = isOwn || svGd == de.oaa.xxx.user.Sichtbarkeit.ALLE
                || (svGd == de.oaa.xxx.user.Sichtbarkeit.NUR_FREUNDE && isFriend);

        // XP nur zurückgeben wenn berechtigt
        de.oaa.xxx.user.Sichtbarkeit svXp = user.getSichtbarkeitXp();
        boolean showXp = isOwn || svXp == de.oaa.xxx.user.Sichtbarkeit.ALLE
                || (svXp == de.oaa.xxx.user.Sichtbarkeit.NUR_FREUNDE && isFriend);

        return new UserProfile(
                user.getUserId(), user.getName(), user.getProfilePicture(), user.getProfilePictureHq(),
                status,
                showGrunddaten ? user.getAlter()            : null,
                showGrunddaten ? user.getGroesse()          : null,
                showGrunddaten ? user.getGewicht()          : null,
                showGrunddaten ? user.getGeschlecht()       : null,
                showGrunddaten ? user.getNeigung()          : null,
                showGrunddaten ? user.getBeziehungsstatus() : null,
                showGrunddaten ? user.getBeschreibung()     : null,
                showXp ? user.getLockeeXp()    : 0,
                showXp ? user.getKeyholderXp() : 0,
                showXp ? user.getBdsmXp()      : 0,
                user.getSichtbarkeitGrunddaten(),
                user.getSichtbarkeitGalerie(),
                user.getSichtbarkeitFreunde(),
                user.getSichtbarkeitFeed(),
                user.getSichtbarkeitPinnwand(),
                user.getSichtbarkeitXp(),
                user.getSichtbarkeitLockhistorie(),
                user.isProfilBeiVeroeffentlichungenSichtbar());
    }

    private MessageDto toMessageDto(MessageEntity m) {
        String senderName = userRepository.findById(m.getSenderId())
                .map(UserEntity::getName)
                .orElse("Unbekannt");
        return new MessageDto(
                m.getMessageId(), m.getSenderId(), senderName,
                m.getReceiverId(), m.getText(), m.getSentAt(), m.getReadAt() != null);
    }
}
