package de.oaa.xxx.search;

import de.oaa.xxx.location.repository.LocationEventRepository;
import de.oaa.xxx.location.repository.LocationRepository;
import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@RequestMapping("/search")
public class SearchController {

    private static final int MAX_LIMIT = 100;

    record UserResult(UUID userId, String name, String profilePicture) {}
    record LocationResult(UUID locationId, String name, String profilePicture) {}
    record EventResult(UUID eventId, UUID locationId, String title, String imageData, String startAt) {}
    record SearchResult(List<UserResult> users, List<LocationResult> locations, List<EventResult> events) {}

    private final UserRepository userRepository;
    private final UserService userService;
    private final LocationRepository locationRepository;
    private final LocationEventRepository locationEventRepository;

    public SearchController(UserRepository userRepository, UserService userService,
                            LocationRepository locationRepository,
                            LocationEventRepository locationEventRepository) {
        this.userRepository = userRepository;
        this.userService = userService;
        this.locationRepository = locationRepository;
        this.locationEventRepository = locationEventRepository;
    }

    /**
     * Unified search endpoint.
     *
     * <p>Parameter:
     * <ul>
     *   <li>{@code q}      – Suchbegriff (min. 2 Zeichen)</li>
     *   <li>{@code limit}  – max. Ergebnisse pro Typ (default 20, max 100)</li>
     *   <li>{@code offset} – Pagination-Offset (default 0) – für die Suche-Seite</li>
     *   <li>{@code type}   – optional: "users" | "locations" | "events" – gibt nur diesen Typ zurück</li>
     * </ul>
     *
     * <p>Für das Topbar-Overlay: limit=3, kein type → alle drei Typen mit je 3 Ergebnissen.
     * <p>Für die Suche-Seite: limit=24, type=users|locations|events, offset für Pagination.
     */
    @GetMapping
    public ResponseEntity<SearchResult> search(
            @RequestParam String q,
            @RequestParam(defaultValue = "20") int limit,
            @RequestParam(defaultValue = "0") int offset,
            @RequestParam(required = false) String type,
            Principal principal) {

        if (q == null || q.isBlank() || q.length() < 2) {
            return ResponseEntity.ok(new SearchResult(List.of(), List.of(), List.of()));
        }

        int safeLimit  = Math.min(Math.max(1, limit), MAX_LIMIT);
        int safePage   = offset > 0 ? offset / safeLimit : 0;
        Pageable page  = PageRequest.of(safePage, safeLimit);

        UUID myId = (principal != null) ? userService.requireUser(principal).getUserId() : null;

        boolean allTypes = type == null || type.isBlank();

        List<UserResult> users = List.of();
        if (allTypes || "users".equals(type)) {
            users = userRepository
                    .findByNameContainingIgnoreCase(q, page)
                    .stream()
                    .filter(u -> myId == null || !u.getUserId().equals(myId))
                    .map(u -> new UserResult(u.getUserId(), u.getName(), u.getProfilePicture()))
                    .toList();
        }

        List<LocationResult> locations = List.of();
        if (allTypes || "locations".equals(type)) {
            locations = locationRepository
                    .findByNameContainingIgnoreCaseOrderByNameAsc(q, page)
                    .stream()
                    .map(l -> new LocationResult(l.getLocationId(), l.getName(), l.getProfilePictureLq()))
                    .toList();
        }

        List<EventResult> events = List.of();
        if (allTypes || "events".equals(type)) {
            LocalDateTime now = LocalDateTime.now();
            events = locationEventRepository
                    .findByTitleContainingIgnoreCase(q, page)
                    .stream()
                    .filter(e -> e.getStartAt() != null && e.getStartAt().isAfter(now))
                    .map(e -> new EventResult(
                            e.getEventId(), e.getLocationId(),
                            e.getTitle(), e.getImageData(),
                            e.getStartAt().toString()))
                    .toList();
        }

        return ResponseEntity.ok(new SearchResult(users, locations, events));
    }
}
