package de.oaa.xxx.user;

import java.security.Principal;
import java.time.LocalDate;
import java.time.Period;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import de.oaa.xxx.games.bdsm.entity.BdsmDefaultsEntity;
import de.oaa.xxx.games.bdsm.repository.BdsmDefaultsRepository;
import de.oaa.xxx.games.chastity.common.BaseLockRepository;
import de.oaa.xxx.games.chastity.common.BaseLockTemplateRepository;
import de.oaa.xxx.games.chastity.common.CodeCreator;
import de.oaa.xxx.games.chastity.ttlock.TTAuthService;
import de.oaa.xxx.games.chastity.ttlock.TTLockConfigRepository;
import de.oaa.xxx.games.chastity.ttlock.TTLockService;
import de.oaa.xxx.games.chastity.ttlock.TTLockUserConfigEntity;
import de.oaa.xxx.games.chastity.ttlock.TTLockUserConfigRepository;
import de.oaa.xxx.registration.Registration;
import de.oaa.xxx.registration.RegistrationRepository;
import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.social.entity.NotificationPreferenceEntity;
import de.oaa.xxx.social.repository.NotificationPreferenceRepository;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;

@RestController
@RequestMapping("/user")
public class UserController {

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

    private final UserRepository userRepository;
    private final RegistrationRepository registrationRepository;
    private final NotificationPreferenceRepository notificationPreferenceRepository;
    private final BdsmDefaultsRepository bdsmDefaultsRepository;
    private final UserService userService;
    private final TTLockUserConfigRepository ttLockUserConfigRepository;
    private final TTLockConfigRepository ttLockConfigRepository;
    private final TTAuthService ttAuthService;
    private final TTLockService ttLockService;
    private final BaseLockRepository baseLockRepository;
    private final BaseLockTemplateRepository baseLockTemplateRepository;

    public UserController(UserRepository userRepository,
                          RegistrationRepository registrationRepository,
                          NotificationPreferenceRepository notificationPreferenceRepository,
                          BdsmDefaultsRepository bdsmDefaultsRepository,
                          UserService userService,
                          TTLockUserConfigRepository ttLockUserConfigRepository,
                          TTLockConfigRepository ttLockConfigRepository,
                          TTAuthService ttAuthService,
                          TTLockService ttLockService,
                          BaseLockRepository baseLockRepository,
                          BaseLockTemplateRepository baseLockTemplateRepository) {
        this.userRepository = userRepository;
        this.registrationRepository = registrationRepository;
        this.notificationPreferenceRepository = notificationPreferenceRepository;
        this.bdsmDefaultsRepository = bdsmDefaultsRepository;
        this.userService = userService;
        this.ttLockUserConfigRepository = ttLockUserConfigRepository;
        this.ttLockConfigRepository = ttLockConfigRepository;
        this.ttAuthService = ttAuthService;
        this.ttLockService = ttLockService;
        this.baseLockRepository = baseLockRepository;
        this.baseLockTemplateRepository = baseLockTemplateRepository;
    }

    record ProfilePictureRequest(String picture, String pictureHq) {}
    record NameChangeRequest(String name) {}
    record GeburtsdatumChangeRequest(LocalDate geburtsdatum) {}
    record TtlockUserConfigDto(String username, boolean passwordSet, Integer lockId, boolean testSuccessful) {}
    record TtlockUserConfigRequest(String username, String password, Integer lockId) {}
    record ProfileRequest(Integer groesse, Integer gewicht,
                          Geschlecht geschlecht, Neigung neigung, Beziehungsstatus beziehungsstatus, String beschreibung) {}
    record DatingRequest(boolean datingAktiv, String datingStadt, Double datingLat, Double datingLon,
                         List<String> datingGeschlechter,
                         Integer datingMaxDistanzKm, Integer datingMinAlter, Integer datingMaxAlter) {}
    record DatingFilterRequest(List<String> datingGeschlechter,
                               Integer datingMaxDistanzKm, Integer datingMinAlter, Integer datingMaxAlter) {}
    record PrivacyRequest(
            Sichtbarkeit sichtbarkeitGrunddaten,
            Sichtbarkeit sichtbarkeitGalerie,
            Sichtbarkeit sichtbarkeitFreunde,
            Sichtbarkeit sichtbarkeitFeed,
            Sichtbarkeit sichtbarkeitPinnwand,
            Sichtbarkeit sichtbarkeitXp,
            Sichtbarkeit sichtbarkeitLockhistorie,
            Sichtbarkeit sichtbarkeitVorlieben,
            Boolean profilBeiVeroeffentlichungenSichtbar) {}

    @PutMapping("/me/dating")
    public ResponseEntity<Void> updateDating(@RequestBody DatingRequest request, Principal principal) {
        if (request.datingAktiv() && (request.datingStadt() == null || request.datingStadt().isBlank())) {
            return ResponseEntity.badRequest().build();
        }
        var user = userService.requireUser(principal);
        user.setDatingAktiv(request.datingAktiv());
        if (!request.datingAktiv()) {
            // Alle Dating-Einstellungen zurücksetzen
            user.setDatingStadt(null);
            user.setDatingLat(null);
            user.setDatingLon(null);
            user.setDatingGeschlechter(null);
            user.setDatingMaxDistanzKm(null);
            user.setDatingMinAlter(null);
            user.setDatingMaxAlter(null);
        } else {
            user.setDatingStadt(request.datingStadt().trim());
            user.setDatingLat(request.datingLat());
            user.setDatingLon(request.datingLon());
            if (request.datingGeschlechter() != null && !request.datingGeschlechter().isEmpty()) {
                String joined = request.datingGeschlechter().stream()
                        .filter(g -> { try { Geschlecht.valueOf(g); return true; } catch (IllegalArgumentException e) { return false; } })
                        .collect(Collectors.joining(","));
                user.setDatingGeschlechter(joined.isBlank() ? null : joined);
            } else {
                user.setDatingGeschlechter(null);
            }
            if (request.datingMaxDistanzKm() != null) user.setDatingMaxDistanzKm(Math.max(1, Math.min(500, request.datingMaxDistanzKm())));
            if (request.datingMinAlter() != null) user.setDatingMinAlter(Math.max(18, Math.min(99, request.datingMinAlter())));
            if (request.datingMaxAlter() != null) user.setDatingMaxAlter(Math.max(18, Math.min(99, request.datingMaxAlter())));
        }
        userRepository.save(user);
        LOGGER.info("User {} hat Dating-Einstellungen aktualisiert: aktiv={}", user.getUserId(), request.datingAktiv());
        return ResponseEntity.ok().build();
    }

    @PutMapping("/me/dating-filter")
    public ResponseEntity<Void> updateDatingFilter(@RequestBody DatingFilterRequest request, Principal principal) {
        var user = userService.requireUser(principal);
        if (!user.isDatingAktiv())
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        if (request.datingGeschlechter() != null) {
            String joined = request.datingGeschlechter().stream()
                    .filter(g -> { try { Geschlecht.valueOf(g); return true; } catch (IllegalArgumentException e) { return false; } })
                    .collect(Collectors.joining(","));
            user.setDatingGeschlechter(joined.isBlank() ? null : joined);
        }
        if (request.datingMaxDistanzKm() != null) user.setDatingMaxDistanzKm(Math.max(1, Math.min(500, request.datingMaxDistanzKm())));
        if (request.datingMinAlter()     != null) user.setDatingMinAlter(Math.max(18, Math.min(99, request.datingMinAlter())));
        if (request.datingMaxAlter()     != null) user.setDatingMaxAlter(Math.max(18, Math.min(99, request.datingMaxAlter())));
        userRepository.save(user);
        return ResponseEntity.ok().build();
    }

    @PutMapping("/me/picture")
    public ResponseEntity<Void> updateProfilePicture(@RequestBody ProfilePictureRequest request, Principal principal) {
        var user = userService.requireUser(principal);
        user.setProfilePicture(request.picture());
        user.setProfilePictureHq(request.pictureHq());
        userRepository.save(user);
        LOGGER.debug("User {} hat Profilbild aktualisiert", user.getUserId());
        return ResponseEntity.ok().build();
    }

    @PutMapping("/me/profile")
    public ResponseEntity<Void> updateProfile(@RequestBody ProfileRequest request, Principal principal) {
        var user = userService.requireUser(principal);
        if (request.beschreibung() != null && request.beschreibung().length() > 600) {
            return ResponseEntity.badRequest().build();
        }
        user.setGroesse(request.groesse());
        user.setGewicht(request.gewicht());
        user.setGeschlecht(request.geschlecht());
        user.setNeigung(request.neigung());
        user.setBeziehungsstatus(request.beziehungsstatus());
        user.setBeschreibung(request.beschreibung());
        userRepository.save(user);
        LOGGER.info("User {} hat Profil aktualisiert", user.getUserId());
        return ResponseEntity.ok().build();
    }

    @PutMapping("/me/privacy")
    public ResponseEntity<Void> updatePrivacy(@RequestBody PrivacyRequest request, Principal principal) {
        var user = userService.requireUser(principal);
        if (request.sichtbarkeitGrunddaten()  != null) user.setSichtbarkeitGrunddaten(request.sichtbarkeitGrunddaten());
        if (request.sichtbarkeitGalerie()     != null) user.setSichtbarkeitGalerie(request.sichtbarkeitGalerie());
        if (request.sichtbarkeitFreunde()     != null) user.setSichtbarkeitFreunde(request.sichtbarkeitFreunde());
        if (request.sichtbarkeitFeed()        != null) user.setSichtbarkeitFeed(request.sichtbarkeitFeed());
        if (request.sichtbarkeitPinnwand()    != null) user.setSichtbarkeitPinnwand(request.sichtbarkeitPinnwand());
        if (request.sichtbarkeitXp()          != null) user.setSichtbarkeitXp(request.sichtbarkeitXp());
        if (request.sichtbarkeitLockhistorie()!= null) user.setSichtbarkeitLockhistorie(request.sichtbarkeitLockhistorie());
        if (request.sichtbarkeitVorlieben()   != null) user.setSichtbarkeitVorlieben(request.sichtbarkeitVorlieben());
        if (request.profilBeiVeroeffentlichungenSichtbar() != null) {
            boolean showAuthor = request.profilBeiVeroeffentlichungenSichtbar();
            user.setProfilBeiVeroeffentlichungenSichtbar(showAuthor);
            // Alle veröffentlichten Templates synchronisieren
            var templates = baseLockTemplateRepository.findByOwnerAndPublishedTrue(user.getUserId());
            for (var t : templates) {
                t.setShowAuthor(showAuthor);
            }
            baseLockTemplateRepository.saveAll(templates);
        }
        userRepository.save(user);
        LOGGER.info("User {} hat Datenschutz-Einstellungen aktualisiert", user.getUserId());
        return ResponseEntity.ok().build();
    }

    record NotificationPreferenceRequest(boolean inApp, boolean email) {}

    @GetMapping("/me/notifications")
    public ResponseEntity<Map<String, Object>> getNotifications(Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();

        Map<String, NotificationPreferenceEntity> byKey = notificationPreferenceRepository.findByUserId(userId)
                .stream().collect(Collectors.toMap(p -> p.getCause().name(), p -> p));

        Map<String, Object> result = new LinkedHashMap<>();
        for (MessageCause cause : MessageCause.values()) {
            NotificationPreferenceEntity pref = byKey.getOrDefault(
                    cause.name(), NotificationPreferenceEntity.defaultFor(userId, cause));
            Map<String, Object> entry = new LinkedHashMap<>();
            entry.put("inApp", pref.isInApp());
            entry.put("email", pref.isEmail());
            result.put(cause.name(), entry);
        }
        return ResponseEntity.ok(result);
    }

    @PutMapping("/me/notifications")
    public ResponseEntity<Void> updateNotifications(@RequestBody Map<String, NotificationPreferenceRequest> request, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();

        for (var entry : request.entrySet()) {
            MessageCause cause;
            try {
                cause = MessageCause.valueOf(entry.getKey());
            } catch (IllegalArgumentException e) {
                continue;
            }
            NotificationPreferenceEntity pref = notificationPreferenceRepository
                    .findByUserIdAndCause(userId, cause)
                    .orElseGet(() -> {
                        NotificationPreferenceEntity n = new NotificationPreferenceEntity();
                        n.setUserId(userId);
                        n.setCause(cause);
                        return n;
                    });
            pref.setInApp(entry.getValue().inApp());
            pref.setEmail(entry.getValue().email());
            notificationPreferenceRepository.save(pref);
        }
        return ResponseEntity.ok().build();
    }

    record BdsmDefaultsRequest(List<String> spieltMit, List<String> rollen, List<String> werkzeuge) {}

    @GetMapping("/me/bdsm-defaults")
    public ResponseEntity<Map<String, Object>> getBdsmDefaults(Principal principal) {
        var currentUser = userService.requireUser(principal);
        UUID userId = currentUser.getUserId();

        BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
                .orElse(new BdsmDefaultsEntity());
        Map<String, Object> result = new java.util.LinkedHashMap<>();
        result.put("geschlecht", currentUser.getGeschlecht() != null ? currentUser.getGeschlecht().name() : null);
        result.put("spieltMit",  splitOrEmpty(d.getSpieltMit()));
        result.put("rollen",     splitOrEmpty(d.getRollen()));
        result.put("werkzeuge",  splitOrEmpty(d.getWerkzeuge()));
        return ResponseEntity.ok(result);
    }

    @GetMapping("/{userId}/bdsm-defaults")
    public ResponseEntity<Map<String, Object>> getBdsmDefaultsForUser(@PathVariable("userId") UUID userId) {
        var userOpt = userRepository.findById(userId);
        if (userOpt.isEmpty()) return ResponseEntity.notFound().build();
        UserEntity user = userOpt.get();
        BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
                .orElse(new BdsmDefaultsEntity());
        Map<String, Object> result = new java.util.LinkedHashMap<>();
        result.put("geschlecht",  user.getGeschlecht() != null ? user.getGeschlecht().name() : null);
        result.put("spieltMit",  splitOrEmpty(d.getSpieltMit()));
        result.put("rollen",     splitOrEmpty(d.getRollen()));
        result.put("werkzeuge",  splitOrEmpty(d.getWerkzeuge()));
        return ResponseEntity.ok(result);
    }

    @PutMapping("/me/bdsm-defaults")
    public ResponseEntity<Void> updateBdsmDefaults(@RequestBody BdsmDefaultsRequest request, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();

        BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
                .orElseGet(() -> { BdsmDefaultsEntity n = new BdsmDefaultsEntity(); n.setUserId(userId); return n; });
        d.setSpieltMit(request.spieltMit() == null ? "" : String.join(",", request.spieltMit()));
        d.setRollen(request.rollen() == null ? "" : String.join(",", request.rollen()));
        d.setWerkzeuge(request.werkzeuge() == null ? "" : String.join(",", request.werkzeuge()));
        bdsmDefaultsRepository.save(d);
        return ResponseEntity.ok().build();
    }

    private static List<String> splitOrEmpty(String s) {
        if (s == null || s.isBlank()) return List.of();
        return List.of(s.split(","));
    }

    @PutMapping("/me/geburtsdatum")
    public ResponseEntity<Void> updateGeburtsdatum(@RequestBody GeburtsdatumChangeRequest request, Principal principal) {
        if (request.geburtsdatum() == null
                || Period.between(request.geburtsdatum(), LocalDate.now()).getYears() < 18) {
            return ResponseEntity.status(422).build();
        }
        var user = userService.requireUser(principal);
        user.setGeburtsdatum(request.geburtsdatum());
        userRepository.save(user);
        LOGGER.info("User {} hat Geburtsdatum aktualisiert", user.getUserId());
        return ResponseEntity.ok().build();
    }

    @PutMapping("/me/name")
    public ResponseEntity<Void> updateName(@RequestBody NameChangeRequest request, Principal principal) {
        String newName = request.name();
        if (userRepository.findByName(newName).isPresent()
                || registrationRepository.findByName(newName).isPresent()) {
            return ResponseEntity.status(409).build();
        }
        var user = userService.requireUser(principal);
        user.setName(newName);
        userRepository.save(user);
        LOGGER.info("User {} hat Namen zu '{}' geändert", user.getUserId(), newName);
        return ResponseEntity.ok().build();
    }

    @DeleteMapping("/me")
    public ResponseEntity<Void> deleteAccount(Principal principal) {
        var currentUser = userService.requireUser(principal);
        UUID userId = currentUser.getUserId();
        String email = currentUser.getEmail();

        userService.deleteAccount(userId, email);

        ResponseCookie cookie = ResponseCookie.from("jwt", "")
                .httpOnly(true)
                .sameSite("Strict")
                .path("/")
                .maxAge(0)
                .build();
        return ResponseEntity.ok()
                .header(HttpHeaders.SET_COOKIE, cookie.toString())
                .build();
    }

    // ── TTLock-Account ────────────────────────────────────────────────────────

    @GetMapping("/me/ttlock")
    public ResponseEntity<TtlockUserConfigDto> getTtlockUserConfig(Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();
        TTLockUserConfigEntity cfg = ttLockUserConfigRepository.findById(userId)
                .orElse(new TTLockUserConfigEntity());
        return ResponseEntity.ok(new TtlockUserConfigDto(
                cfg.getUsername(),
                cfg.getPasswordMd5() != null && !cfg.getPasswordMd5().isBlank(),
                cfg.getLockId(),
                cfg.isTestSuccessful()
        ));
    }

    @PutMapping("/me/ttlock")
    public ResponseEntity<Void> saveTtlockUserConfig(@RequestBody TtlockUserConfigRequest body, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();
        TTLockUserConfigEntity cfg = ttLockUserConfigRepository.findById(userId)
                .orElseGet(() -> { TTLockUserConfigEntity n = new TTLockUserConfigEntity(); n.setUserId(userId); return n; });
        boolean credentialsChanged = !java.util.Objects.equals(cfg.getUsername(), body.username())
                || !java.util.Objects.equals(cfg.getLockId(), body.lockId())
                || (body.password() != null && !body.password().isBlank());
        if (credentialsChanged) {
            cfg.setTestSuccessful(false);
        }
        cfg.setUsername(body.username());
        if (body.password() != null && !body.password().isBlank()) {
            cfg.setPasswordMd5(DigestUtils.md5DigestAsHex(body.password().getBytes(StandardCharsets.UTF_8)));
        }
        cfg.setLockId(body.lockId());
        ttLockUserConfigRepository.save(cfg);
        return ResponseEntity.ok().build();
    }

    @GetMapping("/me/ttlock/test")
    public ResponseEntity<Map<String, Object>> testTtlockConnection(Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();

        var userCfg = ttLockUserConfigRepository.findById(userId).orElse(null);
        if (userCfg == null || userCfg.getUsername() == null || userCfg.getPasswordMd5() == null || userCfg.getLockId() == null) {
            return ResponseEntity.badRequest().body(Map.of("error", "ttlock_not_configured"));
        }
        var adminCfg = ttLockConfigRepository.findById(1L).orElse(null);
        if (adminCfg == null || adminCfg.getClientId() == null || adminCfg.getClientSecret() == null) {
            return ResponseEntity.badRequest().body(Map.of("error", "admin_config_missing"));
        }

        String token = ttAuthService.getAccessToken(
                adminCfg.getClientId(), adminCfg.getClientSecret(),
                userCfg.getUsername(), userCfg.getPasswordMd5());
        if (token == null) {
            return ResponseEntity.status(502).body(Map.of("error", "auth_failed"));
        }

        TTLockService.TTLockDetailResponse detail = ttLockService.getLockDetail(
                adminCfg.getClientId(), token, userCfg.getLockId());
        if (detail == null || detail.getErrcode() != 0) {
            String msg = detail != null ? detail.getErrmsg() : "Keine Antwort";
            return ResponseEntity.status(502).body(Map.of("error", "lock_detail_failed", "message", msg));
        }

        userCfg.setTestSuccessful(true);
        ttLockUserConfigRepository.save(userCfg);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("lockId",          detail.getLockId());
        result.put("lockName",        detail.getLockName());
        result.put("lockAlias",       detail.getLockAlias());
        result.put("modelNum",        detail.getModelNum());
        result.put("electricQuantity", detail.getElectricQuantity());
        result.put("state",           detail.getState());
        return ResponseEntity.ok(result);
    }

    @PostMapping("/me/ttlock/open")
    public ResponseEntity<Map<String, Object>> ttlockOpen(Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();

        var userCfg = ttLockUserConfigRepository.findById(userId).orElse(null);
        if (userCfg == null || userCfg.getUsername() == null || userCfg.getPasswordMd5() == null || userCfg.getLockId() == null) {
            return ResponseEntity.badRequest().body(Map.of("error", "ttlock_not_configured"));
        }
        var adminCfg = ttLockConfigRepository.findById(1L).orElse(null);
        if (adminCfg == null || adminCfg.getClientId() == null) {
            return ResponseEntity.badRequest().body(Map.of("error", "admin_config_missing"));
        }

        var activeLock = baseLockRepository.findByLockee(userId);
        if (activeLock.isPresent() && activeLock.get().getUnlockTime() == null) {
            return ResponseEntity.status(409).body(Map.of("error", "active_lock_exists"));
        }

        String token = ttAuthService.getAccessToken(
                adminCfg.getClientId(), adminCfg.getClientSecret(),
                userCfg.getUsername(), userCfg.getPasswordMd5());
        if (token == null) {
            return ResponseEntity.status(502).body(Map.of("error", "auth_failed"));
        }

        String pin = CodeCreator.createNumeric(6);
        Integer pwdId = ttLockService.addCustomPasscode(adminCfg.getClientId(), token, userCfg.getLockId(), pin);
        if (pwdId == null) {
            return ResponseEntity.status(502).body(Map.of("error", "passcode_failed"));
        }

        return ResponseEntity.ok(Map.of("pin", pin, "keyboardPwdId", pwdId));
    }

    @DeleteMapping("/me/ttlock/open/{keyboardPwdId}")
    public ResponseEntity<Void> ttlockCloseOpen(@PathVariable("keyboardPwdId") int keyboardPwdId, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();

        var userCfg = ttLockUserConfigRepository.findById(userId).orElse(null);
        if (userCfg == null || userCfg.getLockId() == null) return ResponseEntity.badRequest().build();
        var adminCfg = ttLockConfigRepository.findById(1L).orElse(null);
        if (adminCfg == null) return ResponseEntity.badRequest().build();

        String token = ttAuthService.getAccessToken(
                adminCfg.getClientId(), adminCfg.getClientSecret(),
                userCfg.getUsername(), userCfg.getPasswordMd5());
        if (token == null) return ResponseEntity.status(502).build();

        ttLockService.deleteCustomPasscode(adminCfg.getClientId(), token, userCfg.getLockId(), keyboardPwdId);
        return ResponseEntity.ok().build();
    }

    @PostMapping
    public ResponseEntity<Void> userAnlegen(@RequestBody Registration registration) {
        try {
            userService.createUser(registration);
            return ResponseEntity.status(201).build();
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest().build();
        } catch (IllegalStateException e) {
            return ResponseEntity.status(409).build();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            return ResponseEntity.internalServerError().build();
        }
    }

    record LocationFilterRequest(String filterCity, Double filterLat, Double filterLon, Integer filterMaxDistKm) {}

    @PutMapping("/me/location-filter")
    public ResponseEntity<Void> updateLocationFilter(@RequestBody LocationFilterRequest request, Principal principal) {
        var user = userService.requireUser(principal);
        if (request.filterCity() != null) user.setFilterCity(request.filterCity());
        if (request.filterLat()  != null) user.setFilterLat(request.filterLat());
        if (request.filterLon()  != null) user.setFilterLon(request.filterLon());
        if (request.filterMaxDistKm() != null) user.setFilterMaxDistKm(Math.max(1, Math.min(500, request.filterMaxDistKm())));
        userRepository.save(user);
        return ResponseEntity.ok().build();
    }
}
