package com.android.trisolarisserver.controller import com.android.trisolarisserver.component.PropertyAccess import com.android.trisolarisserver.controller.dto.PaymentCreateRequest import com.android.trisolarisserver.controller.dto.PaymentResponse import com.android.trisolarisserver.db.repo.BookingRepo import com.android.trisolarisserver.models.booking.Payment import com.android.trisolarisserver.models.booking.PaymentMethod import com.android.trisolarisserver.models.property.Role import com.android.trisolarisserver.repo.AppUserRepo import com.android.trisolarisserver.repo.PaymentRepo import com.android.trisolarisserver.repo.PropertyRepo 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.time.LocalDate import java.time.OffsetDateTime import java.util.UUID @RestController @RequestMapping("/properties/{propertyId}/bookings/{bookingId}/payments") class Payments( private val propertyAccess: PropertyAccess, private val propertyRepo: PropertyRepo, private val bookingRepo: BookingRepo, private val paymentRepo: PaymentRepo, private val appUserRepo: AppUserRepo ) { @PostMapping @ResponseStatus(HttpStatus.CREATED) fun create( @PathVariable propertyId: UUID, @PathVariable bookingId: UUID, @AuthenticationPrincipal principal: MyPrincipal?, @RequestBody request: PaymentCreateRequest ): PaymentResponse { val actor = requireRole(propertyAccess, propertyId, principal, Role.ADMIN, Role.MANAGER, Role.STAFF) val property = propertyRepo.findById(propertyId).orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND, "Property not found") } val booking = bookingRepo.findById(bookingId).orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found") } if (booking.property.id != propertyId) { throw ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found for property") } if (request.amount <= 0) { throw ResponseStatusException(HttpStatus.BAD_REQUEST, "amount must be > 0") } val method = parseMethod(request.method) val receivedAt = parseOffset(request.receivedAt) ?: OffsetDateTime.now() val payment = Payment( property = property, booking = booking, amount = request.amount, currency = request.currency ?: property.currency, method = method, reference = request.reference?.trim()?.ifBlank { null }, notes = request.notes?.trim()?.ifBlank { null }, receivedAt = receivedAt, receivedBy = appUserRepo.findById(actor.userId).orElse(null) ) return paymentRepo.save(payment).toResponse() } @GetMapping fun list( @PathVariable propertyId: UUID, @PathVariable bookingId: UUID, @AuthenticationPrincipal principal: MyPrincipal? ): List { requireMember(propertyAccess, propertyId, principal) val booking = bookingRepo.findById(bookingId).orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found") } if (booking.property.id != propertyId) { throw ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found for property") } return paymentRepo.findByBookingIdOrderByReceivedAtDesc(bookingId).map { it.toResponse() } } private fun parseMethod(value: String): PaymentMethod { return try { PaymentMethod.valueOf(value.trim()) } catch (_: IllegalArgumentException) { throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Unknown payment method") } } } private fun Payment.toResponse(): PaymentResponse { val id = id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Payment id missing") val bookingId = booking.id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Booking id missing") return PaymentResponse( id = id, bookingId = bookingId, amount = amount, currency = currency, method = method.name, reference = reference, notes = notes, receivedAt = receivedAt.toString(), receivedByUserId = receivedBy?.id ) }