package de.oaa.xxx.dating;

import de.oaa.xxx.social.SseService;
import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.Geschlecht;
import de.oaa.xxx.user.Neigung;
import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import de.oaa.xxx.vorlieben.UserVorliebeRepository;
import de.oaa.xxx.vorlieben.VorliebeItemEntity;
import de.oaa.xxx.vorlieben.VorliebeItemRepository;
import de.oaa.xxx.vorlieben.VorliebeBewertung;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

import java.security.Principal;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/dating")
public class DatingController {

    private static final Set<VorliebeBewertung> POSITIVE_RATINGS = Set.of(
            VorliebeBewertung.MAG_ICH, VorliebeBewertung.UNBEDINGT, VorliebeBewertung.WILL_AUSPROBIEREN);

    private final DatingService datingService;
    private final UserService userService;
    private final UserRepository userRepository;
    private final DatingPassRepository passRepository;
    private final SubscriptionLimitService subscriptionLimitService;
    private final SseService sseService;
    private final UserVorliebeRepository userVorliebeRepository;
    private final VorliebeItemRepository vorliebeItemRepository;

    public DatingController(DatingService datingService, UserService userService,
                             UserRepository userRepository,
                             DatingPassRepository passRepository,
                             SubscriptionLimitService subscriptionLimitService,
                             SseService sseService,
                             UserVorliebeRepository userVorliebeRepository,
                             VorliebeItemRepository vorliebeItemRepository) {
        this.datingService = datingService;
        this.userService = userService;
        this.userRepository = userRepository;
        this.passRepository = passRepository;
        this.subscriptionLimitService = subscriptionLimitService;
        this.sseService = sseService;
        this.userVorliebeRepository = userVorliebeRepository;
        this.vorliebeItemRepository = vorliebeItemRepository;
    }

    record VorliebeChipDto(String name, String bewertung) {}

    // ── Profilsuche ───────────────────────────────────────────────────────────

    @GetMapping("/profile-ids")
    public ResponseEntity<DatingService.IdsResult> getProfileIds(
            @RequestParam(name = "maxDistanceKm", defaultValue = "50")    int maxDistanceKm,
            @RequestParam(name = "minAge",         defaultValue = "18")    int minAge,
            @RequestParam(name = "maxAge",         defaultValue = "99")    int maxAge,
            @RequestParam(name = "geschlechter",   required = false)       List<String> geschlechter,
            @RequestParam(name = "neigungen",      required = false)       List<String> neigungen,
            @RequestParam(name = "vorliebenIds",   required = false)       List<UUID> vorliebenIds,
            @RequestParam(name = "vorliebenUnd",   defaultValue = "false") boolean vorliebenUnd,
            Principal principal) {

        UserEntity me = requireDatingUser(principal);

        DatingService.DatingFilter filter = new DatingService.DatingFilter(
                Math.max(maxDistanceKm, 1),
                Math.max(minAge, 0),
                Math.min(maxAge, 120),
                parseEnumList(geschlechter, Geschlecht.class),
                parseEnumList(neigungen, Neigung.class),
                vorliebenIds != null ? vorliebenIds : List.of(),
                vorliebenUnd
        );

        return ResponseEntity.ok(datingService.findSortedIds(
                me.getUserId(), filter, me.getDatingLat(), me.getDatingLon()));
    }

    @PostMapping("/profiles/batch")
    public ResponseEntity<List<DatingService.DatingProfileDto>> getProfileBatch(
            @RequestBody List<UUID> ids,
            Principal principal) {

        UserEntity me = requireDatingUser(principal);
        if (ids == null || ids.isEmpty()) return ResponseEntity.ok(List.of());
        List<UUID> capped = ids.stream().filter(Objects::nonNull).limit(50).toList();

        return ResponseEntity.ok(datingService.getProfilesByIds(
                capped, me.getUserId(), me.getDatingLat(), me.getDatingLon()));
    }

    @GetMapping("/discovery")
    public ResponseEntity<List<UUID>> getDiscovery(Principal principal) {
        UserEntity me = requireDatingUser(principal);
        return ResponseEntity.ok(datingService.findDiscoveryIds(me));
    }

    @PostMapping("/pass/{targetId}")
    public ResponseEntity<Void> passProfile(@PathVariable("targetId") UUID targetId, Principal principal) {
        UserEntity me = userService.requireUser(principal);
        if (me.getUserId().equals(targetId)) return ResponseEntity.badRequest().build();
        if (!passRepository.existsByPasserIdAndPassedId(me.getUserId(), targetId)) {
            DatingPassEntity pass = new DatingPassEntity();
            pass.setPassId(java.util.UUID.randomUUID());
            pass.setPasserId(me.getUserId());
            pass.setPassedId(targetId);
            pass.setPassedAt(java.time.LocalDateTime.now());
            passRepository.save(pass);
        }
        return ResponseEntity.status(201).build();
    }

    // ── Likes ─────────────────────────────────────────────────────────────────

    /**
     * Toggelt einen Like auf ein Profil (Like wenn nicht vorhanden, Unlike wenn vorhanden).
     * Gibt zurück ob jetzt geliked wird und ob ein neues Match entstanden ist.
     * Setzt Dating-Aktiv beim eigenen User voraus; beim Ziel-User wird es nicht geprüft
     * (damit auch Profil-Seiten außerhalb der Dating-Suche geliked werden können).
     */
    @PostMapping("/like/{targetId}")
    public ResponseEntity<DatingService.LikeResult> toggleLike(
            @PathVariable("targetId") UUID targetId,
            Principal principal) {

        UserEntity me = userService.requireUser(principal);
        if (!me.isDatingAktiv()) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Dating nicht aktiviert");
        }
        if (me.getUserId().equals(targetId)) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Eigenes Profil kann nicht geliked werden");
        }
        DatingService.LikeResult result = datingService.toggleLike(me.getUserId(), targetId);
        if (result.liked() && result.newMatch()) {
            userRepository.findById(targetId).ifPresent(target -> {
                String myPic    = me.getProfilePicture()     != null ? me.getProfilePicture()     : "";
                String theirPic = target.getProfilePicture() != null ? target.getProfilePicture() : "";
                sseService.push(targetId,        "MATCH", Map.of("partnerId", me.getUserId().toString(),  "partnerName", me.getName(),      "partnerPicture", myPic));
                sseService.push(me.getUserId(),  "MATCH", Map.of("partnerId", targetId.toString(),        "partnerName", target.getName(),  "partnerPicture", theirPic));
            });
        }
        return ResponseEntity.ok(result);
    }

    /**
     * Alle User-IDs, die der eingeloggte User geliked hat.
     * Wird vom Profil-Frontend genutzt um den Like-Status initial zu setzen.
     */
    @GetMapping("/liked-by-me")
    public ResponseEntity<List<UUID>> getLikedByMe(Principal principal) {
        UserEntity me = userService.requireUser(principal);
        return ResponseEntity.ok(datingService.getLikedByMe(me.getUserId()));
    }

    /**
     * Wer hat mich geliked? Premium-geschützt:
     * - Premium: volle Profildaten (userId, name, Bild)
     * - Kein Premium: nur Profilbild (anonymisiert), userId + name = null
     */
    @GetMapping("/who-likes-me")
    public ResponseEntity<DatingService.WhoLikesMeResult> whoLikesMe(Principal principal) {
        UserEntity me = userService.requireUser(principal);
        boolean premium = subscriptionLimitService.hasActivePaidSubscription(me.getUserId());
        return ResponseEntity.ok(datingService.whoLikesMe(me.getUserId(), premium));
    }

    /**
     * Alle Matches des eingeloggten Users (gegenseitige Likes).
     */
    @GetMapping("/matches")
    public ResponseEntity<List<DatingService.MatchDto>> getMatches(Principal principal) {
        UserEntity me = userService.requireUser(principal);
        return ResponseEntity.ok(datingService.getMatches(me.getUserId()));
    }

    /**
     * Prüft ob ein Match mit dem angegebenen User besteht.
     */
    @GetMapping("/match/{userId}")
    public ResponseEntity<Map<String, Boolean>> checkMatch(
            @PathVariable UUID userId, Principal principal) {
        UserEntity me = userService.requireUser(principal);
        boolean isMatch = datingService.isMatch(me.getUserId(), userId);
        return ResponseEntity.ok(Map.of("isMatch", isMatch));
    }

    /**
     * Match mit dem angegebenen User auflösen (beide Likes werden entfernt).
     */
    @DeleteMapping("/match/{userId}")
    public ResponseEntity<Void> unmatch(
            @PathVariable UUID userId, Principal principal) {
        UserEntity me = userService.requireUser(principal);
        datingService.unmatch(me.getUserId(), userId);
        return ResponseEntity.noContent().build();
    }

    /**
     * Gibt bis zu 5 zufällige positive Vorlieben (MAG_ICH, UNBEDINGT, WILL_AUSPROBIEREN)
     * eines Users zurück – für die Profilvorschau im Dating-Bereich.
     */
    @GetMapping("/vorlieben/{userId}")
    public ResponseEntity<List<VorliebeChipDto>> getDatingVorlieben(
            @PathVariable UUID userId, Principal principal) {
        userService.requireUser(principal);

        var positive = userVorliebeRepository.findByUserId(userId).stream()
                .filter(uv -> POSITIVE_RATINGS.contains(uv.getBewertung()))
                .collect(Collectors.toList());

        if (positive.isEmpty()) return ResponseEntity.ok(List.of());

        Collections.shuffle(positive);
        List<UUID> sampleIds = positive.stream()
                .limit(5)
                .map(de.oaa.xxx.vorlieben.UserVorliebeEntity::getItemId)
                .toList();

        Map<UUID, String> nameById = vorliebeItemRepository.findAllById(sampleIds).stream()
                .collect(Collectors.toMap(VorliebeItemEntity::getItemId, VorliebeItemEntity::getName));

        List<VorliebeChipDto> result = positive.stream()
                .limit(5)
                .filter(uv -> nameById.containsKey(uv.getItemId()))
                .map(uv -> new VorliebeChipDto(nameById.get(uv.getItemId()), uv.getBewertung().name()))
                .toList();

        return ResponseEntity.ok(result);
    }

    // ── Helpers ───────────────────────────────────────────────────────────────

    private UserEntity requireDatingUser(Principal principal) {
        UserEntity me = userService.requireUser(principal);
        if (!me.isDatingAktiv()) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Dating nicht aktiviert");
        }
        if (me.getDatingLat() == null || me.getDatingLon() == null) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Standort nicht gesetzt");
        }
        return me;
    }

    private <E extends Enum<E>> List<E> parseEnumList(List<String> values, Class<E> enumClass) {
        if (values == null || values.isEmpty()) return List.of();
        return values.stream()
                .map(s -> {
                    try { return Enum.valueOf(enumClass, s); }
                    catch (IllegalArgumentException e) { return null; }
                })
                .filter(Objects::nonNull)
                .toList();
    }
}
