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.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.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;

@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;

    public UserController(UserRepository userRepository,
                          RegistrationRepository registrationRepository,
                          NotificationPreferenceRepository notificationPreferenceRepository,
                          BdsmDefaultsRepository bdsmDefaultsRepository,
                          UserService userService) {
        this.userRepository = userRepository;
        this.registrationRepository = registrationRepository;
        this.notificationPreferenceRepository = notificationPreferenceRepository;
        this.bdsmDefaultsRepository = bdsmDefaultsRepository;
        this.userService = userService;
    }

    record ProfilePictureRequest(String picture, String pictureHq) {}
    record NameChangeRequest(String name) {}
    record GeburtsdatumChangeRequest(LocalDate geburtsdatum) {}
    record ProfileRequest(Integer groesse, Integer gewicht,
                          Geschlecht geschlecht, Neigung neigung, Beziehungsstatus beziehungsstatus, String beschreibung) {}
    record PrivacyRequest(
            Sichtbarkeit sichtbarkeitGrunddaten,
            Sichtbarkeit sichtbarkeitGalerie,
            Sichtbarkeit sichtbarkeitFreunde,
            Sichtbarkeit sichtbarkeitFeed,
            Sichtbarkeit sichtbarkeitPinnwand,
            Sichtbarkeit sichtbarkeitXp,
            Sichtbarkeit sichtbarkeitLockhistorie) {}

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

    @PutMapping("/me/profile")
    public ResponseEntity<Void> updateProfile(@RequestBody ProfileRequest request, Principal principal) {
        var userOpt = userRepository.findByEmail(principal.getName());
        if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
        var user = userOpt.get();
        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 userOpt = userRepository.findByEmail(principal.getName());
        if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
        var user = userOpt.get();
        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());
        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) {
        var userOpt = userRepository.findByEmail(principal.getName());
        if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
        UUID userId = userOpt.get().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) {
        var userOpt = userRepository.findByEmail(principal.getName());
        if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
        UUID userId = userOpt.get().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 userOpt = userRepository.findByEmail(principal.getName());
        if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
        UUID userId = userOpt.get().getUserId();

        BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
                .orElse(new BdsmDefaultsEntity());
        Map<String, Object> result = new java.util.LinkedHashMap<>();
        result.put("geschlecht", userOpt.get().getGeschlecht() != null ? userOpt.get().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 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) {
        var userOpt = userRepository.findByEmail(principal.getName());
        if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
        UUID userId = userOpt.get().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 userOpt = userRepository.findByEmail(principal.getName());
        if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
        var user = userOpt.get();
        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 = userRepository.findByEmail(principal.getName());
        if (user.isEmpty()) return ResponseEntity.status(401).build();
        user.get().setName(newName);
        userRepository.save(user.get());
        LOGGER.info("User {} hat Namen zu '{}' geändert", user.get().getUserId(), newName);
        return ResponseEntity.ok().build();
    }

    @DeleteMapping("/me")
    public ResponseEntity<Void> deleteAccount(Principal principal) {
        var userOpt = userRepository.findByEmail(principal.getName());
        if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
        UUID userId = userOpt.get().getUserId();
        String email = userOpt.get().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();
    }

    @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();
        }
    }
}
