package com.android.trisolarisserver.controller import com.android.trisolarisserver.component.PropertyAccess import com.android.trisolarisserver.controller.dto.GuestRatingCreateRequest import com.android.trisolarisserver.controller.dto.GuestRatingResponse import com.android.trisolarisserver.db.repo.BookingRepo import com.android.trisolarisserver.db.repo.GuestRatingRepo import com.android.trisolarisserver.db.repo.GuestRepo import com.android.trisolarisserver.repo.AppUserRepo import com.android.trisolarisserver.repo.PropertyRepo import com.android.trisolarisserver.models.booking.GuestRating import com.android.trisolarisserver.models.booking.GuestRatingScore import com.android.trisolarisserver.security.MyPrincipal import org.springframework.http.HttpStatus import org.springframework.security.core.annotation.AuthenticationPrincipal 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.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.bind.annotation.RestController import org.springframework.web.server.ResponseStatusException import java.util.UUID @RestController @RequestMapping("/properties/{propertyId}/guests/{guestId}/ratings") class GuestRatings( private val propertyAccess: PropertyAccess, private val propertyRepo: PropertyRepo, private val guestRepo: GuestRepo, private val bookingRepo: BookingRepo, private val guestRatingRepo: GuestRatingRepo, private val appUserRepo: AppUserRepo ) { @PostMapping @ResponseStatus(HttpStatus.CREATED) fun create( @PathVariable propertyId: UUID, @PathVariable guestId: UUID, @AuthenticationPrincipal principal: MyPrincipal?, @RequestBody request: GuestRatingCreateRequest ): GuestRatingResponse { val resolved = requireMember(propertyAccess, propertyId, principal) val (property, guest) = requirePropertyGuest(propertyRepo, guestRepo, propertyId, guestId) val booking = bookingRepo.findById(request.bookingId).orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found") } if (booking.property.id != property.id) { throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Booking not in property") } if (booking.primaryGuest?.id != guest.id) { throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Booking not linked to guest") } if (guestRatingRepo.existsByGuestIdAndBookingId(guest.id!!, booking.id!!)) { throw ResponseStatusException(HttpStatus.CONFLICT, "Rating already exists for booking") } val score = parseScore(request.score) val rating = GuestRating( property = property, guest = guest, booking = booking, score = score, notes = request.notes?.trim(), createdBy = appUserRepo.findById(resolved.userId).orElse(null) ) guestRatingRepo.save(rating) return rating.toResponse() } @GetMapping fun list( @PathVariable propertyId: UUID, @PathVariable guestId: UUID, @AuthenticationPrincipal principal: MyPrincipal? ): List { requireMember(propertyAccess, propertyId, principal) val (_, guest) = requirePropertyGuest(propertyRepo, guestRepo, propertyId, guestId) return guestRatingRepo.findByGuestIdOrderByCreatedAtDesc(guestId).map { it.toResponse() } } private fun parseScore(raw: String): GuestRatingScore { val normalized = raw.trim().uppercase() return when (normalized) { "1", "GOOD" -> GuestRatingScore.GOOD "2", "OK" -> GuestRatingScore.OK "3", "TROUBLE", "TROUBLEMAKER", "TROUBLE-MAKER" -> GuestRatingScore.TROUBLE else -> throw ResponseStatusException(HttpStatus.BAD_REQUEST, "score must be GOOD/OK/TROUBLE or 1/2/3") } } private fun GuestRating.toResponse(): GuestRatingResponse { return GuestRatingResponse( id = id!!, propertyId = property.id!!, guestId = guest.id!!, bookingId = booking.id!!, score = score.name, notes = notes, createdAt = createdAt.toString(), createdByUserId = createdBy?.id ) } }