package de.oaa.xxx.location;

import de.oaa.xxx.location.entity.LocationAdminEntity;
import de.oaa.xxx.location.repository.LocationAdminRepository;
import de.oaa.xxx.location.repository.LocationRepository;
import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import jakarta.transaction.Transactional;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("/locations/{locationId}/admins")
public class LocationAdminController {

    record AdminDto(UUID userId, String name, String profilePicture, boolean isOwner) {}
    record AddAdminRequest(UUID userId) {}
    record TransferOwnerRequest(UUID userId) {}

    private final LocationRepository     locationRepo;
    private final LocationAdminRepository adminRepo;
    private final UserRepository          userRepo;
    private final UserService             userService;

    public LocationAdminController(LocationRepository locationRepo,
                                   LocationAdminRepository adminRepo,
                                   UserRepository userRepo,
                                   UserService userService) {
        this.locationRepo = locationRepo;
        this.adminRepo    = adminRepo;
        this.userRepo     = userRepo;
        this.userService  = userService;
    }

    // ── Hilfsmethoden ────────────────────────────────────────────────────────

    private boolean isAdmin(UUID locationId, UUID userId, de.oaa.xxx.location.entity.LocationEntity loc) {
        return loc.getOwnerId().equals(userId)
                || adminRepo.existsByLocationIdAndUserId(locationId, userId);
    }

    private AdminDto toDto(UUID userId, de.oaa.xxx.location.entity.LocationEntity loc) {
        return userRepo.findById(userId).map(u -> new AdminDto(
                u.getUserId(), u.getName(), u.getProfilePicture(),
                u.getUserId().equals(loc.getOwnerId())))
                .orElse(null);
    }

    // ── Admins auflisten ─────────────────────────────────────────────────────

    @GetMapping
    public ResponseEntity<List<AdminDto>> list(
            @PathVariable UUID locationId,
            Principal principal) {

        userService.requireUser(principal);
        var locOpt = locationRepo.findById(locationId);
        if (locOpt.isEmpty()) return ResponseEntity.notFound().build();
        var loc = locOpt.get();

        // Inhaber ist immer erster Eintrag
        List<AdminDto> admins = new java.util.ArrayList<>();
        userRepo.findById(loc.getOwnerId()).ifPresent(owner ->
                admins.add(new AdminDto(owner.getUserId(), owner.getName(), owner.getProfilePicture(), true)));

        adminRepo.findByLocationId(locationId).stream()
                .filter(a -> !a.getUserId().equals(loc.getOwnerId())) // Inhaber nicht doppelt
                .map(a -> toDto(a.getUserId(), loc))
                .filter(java.util.Objects::nonNull)
                .forEach(admins::add);

        return ResponseEntity.ok(admins);
    }

    // ── Admin hinzufügen ──────────────────────────────────────────────────────

    @PostMapping
    public ResponseEntity<AdminDto> add(
            @PathVariable UUID locationId,
            @RequestBody AddAdminRequest req,
            Principal principal) {

        UUID myId = userService.requireUser(principal).getUserId();
        var locOpt = locationRepo.findById(locationId);
        if (locOpt.isEmpty()) return ResponseEntity.notFound().build();
        var loc = locOpt.get();

        if (!isAdmin(locationId, myId, loc)) return ResponseEntity.status(403).build();
        if (req.userId() == null || !userRepo.existsById(req.userId()))
            return ResponseEntity.badRequest().build();

        // Inhaber muss nicht eingetragen werden
        if (req.userId().equals(loc.getOwnerId()))
            return ResponseEntity.badRequest().build();

        // Bereits Admin?
        if (adminRepo.existsByLocationIdAndUserId(locationId, req.userId()))
            return ResponseEntity.status(409).build();

        LocationAdminEntity entity = new LocationAdminEntity();
        entity.setAdminId(UUID.randomUUID());
        entity.setLocationId(locationId);
        entity.setUserId(req.userId());
        entity.setAddedAt(LocalDateTime.now());
        adminRepo.save(entity);

        return ResponseEntity.status(201).body(toDto(req.userId(), loc));
    }

    // ── Admin entfernen ───────────────────────────────────────────────────────

    @Transactional
    @DeleteMapping("/{userId}")
    public ResponseEntity<Void> remove(
            @PathVariable UUID locationId,
            @PathVariable UUID userId,
            Principal principal) {

        UUID myId = userService.requireUser(principal).getUserId();
        var locOpt = locationRepo.findById(locationId);
        if (locOpt.isEmpty()) return ResponseEntity.notFound().build();
        var loc = locOpt.get();

        if (!isAdmin(locationId, myId, loc)) return ResponseEntity.status(403).build();

        // Inhaber darf nicht entfernt werden
        if (userId.equals(loc.getOwnerId())) return ResponseEntity.status(403).build();

        adminRepo.deleteByLocationIdAndUserId(locationId, userId);
        return ResponseEntity.noContent().build();
    }

    // ── Inhaberwechsel ────────────────────────────────────────────────────────

    @Transactional
    @PutMapping("/transfer-owner")
    public ResponseEntity<Map<String, Object>> transferOwner(
            @PathVariable UUID locationId,
            @RequestBody TransferOwnerRequest req,
            Principal principal) {

        UUID myId = userService.requireUser(principal).getUserId();
        var locOpt = locationRepo.findById(locationId);
        if (locOpt.isEmpty()) return ResponseEntity.notFound().build();
        var loc = locOpt.get();

        // Nur der aktuelle Inhaber darf übertragen
        if (!loc.getOwnerId().equals(myId)) return ResponseEntity.status(403).build();
        if (req.userId() == null || !userRepo.existsById(req.userId()))
            return ResponseEntity.badRequest().build();
        if (req.userId().equals(myId)) return ResponseEntity.badRequest().build();

        // Neuer Inhaber als Admin eintragen (falls noch nicht), alter Inhaber wird normaler Admin
        if (!adminRepo.existsByLocationIdAndUserId(locationId, myId)) {
            LocationAdminEntity a = new LocationAdminEntity();
            a.setAdminId(UUID.randomUUID());
            a.setLocationId(locationId);
            a.setUserId(myId);
            a.setAddedAt(LocalDateTime.now());
            adminRepo.save(a);
        }
        // Neuen Inhaber aus Admin-Liste entfernen (er ist jetzt Owner)
        adminRepo.deleteByLocationIdAndUserId(locationId, req.userId());

        loc.setOwnerId(req.userId());
        locationRepo.save(loc);

        return ResponseEntity.ok(Map.of("newOwnerId", req.userId()));
    }
}
