package com.mvrent.carrental.controller;

import com.mvrent.carrental.model.Booking;
import com.mvrent.carrental.model.Invoice;
import com.mvrent.carrental.model.User;
import com.mvrent.carrental.service.BookingService;
import com.mvrent.carrental.service.CarService;
import com.mvrent.carrental.service.ConfigService;
import com.mvrent.carrental.service.DriverService;
import com.mvrent.carrental.service.PaymentService;
import com.mvrent.carrental.service.PdfService;
import com.mvrent.carrental.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.Map;

@Controller
@RequiredArgsConstructor
public class BookingController {

    private final BookingService bookingService;
    private final CarService carService;
    private final DriverService driverService;
    private final UserService userService;
    private final PdfService pdfService;
    private final ConfigService configService;
    private final PaymentService paymentService;

    @GetMapping("/cars/{carId}/book")
    public String newBooking(@PathVariable Long carId,
                             @RequestParam(required = false) Boolean withDriver,
                             @RequestParam(required = false)
                             @org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE) java.time.LocalDate pickup,
                             @RequestParam(required = false)
                             @org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE) java.time.LocalDate ret,
                             Model model) {
        model.addAttribute("car", carService.findById(carId));
        if (!model.containsAttribute("booking")) {
            Booking booking = new Booking();
            if (withDriver != null) booking.setWithDriver(withDriver);
            if (pickup != null) booking.setPickupDate(pickup);
            if (ret != null) booking.setReturnDate(ret);
            model.addAttribute("booking", booking);
        }
        model.addAttribute("drivers", driverService.findAvailable());
        return "bookings/form";
    }

    @PostMapping("/cars/{carId}/book")
    public String createBooking(@PathVariable Long carId,
                                @Valid @ModelAttribute("booking") Booking booking,
                                BindingResult result,
                                @RequestParam(required = false) Long driverId,
                                @AuthenticationPrincipal UserDetails principal,
                                RedirectAttributes ra,
                                Model model) {
        if (result.hasErrors()) {
            model.addAttribute("car", carService.findById(carId));
            model.addAttribute("drivers", driverService.findAvailable());
            return "bookings/form";
        }
        try {
            User user = userService.findByUsername(principal.getUsername());
            Booking saved = bookingService.createBooking(user, carId, booking, driverId);
            ra.addFlashAttribute("success", "Booking confirmed (id #" + saved.getId() + ")");
            return "redirect:/my-bookings";
        } catch (RuntimeException ex) {
            model.addAttribute("car", carService.findById(carId));
            model.addAttribute("drivers", driverService.findAvailable());
            model.addAttribute("error", ex.getMessage());
            return "bookings/form";
        }
    }

    @GetMapping("/my-bookings")
    public String myBookings(@AuthenticationPrincipal UserDetails principal, Model model) {
        User user = userService.findByUsername(principal.getUsername());
        model.addAttribute("bookings", bookingService.findByUser(user));
        return "bookings/my";
    }

    @GetMapping("/my-bookings/{id}/invoice")
    public String invoice(@PathVariable Long id,
                          @AuthenticationPrincipal UserDetails principal,
                          Model model) {
        User user = userService.findByUsername(principal.getUsername());
        Booking booking = bookingService.findById(id);
        if (!booking.getUser().getId().equals(user.getId())) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Not your booking");
        }
        java.math.BigDecimal paid = paymentService.totalPaid(id);
        java.math.BigDecimal total = booking.getTotalAmount() != null ? booking.getTotalAmount() : java.math.BigDecimal.ZERO;
        model.addAttribute("booking", booking);
        model.addAttribute("invoice", bookingService.invoiceFor(id));
        model.addAttribute("amountPaid", paid);
        model.addAttribute("balance", total.subtract(paid));
        model.addAttribute("pdfUrl", "/my-bookings/" + id + "/invoice/pdf");
        return "bookings/invoice";
    }

    @GetMapping("/my-bookings/{id}/invoice/pdf")
    public ResponseEntity<byte[]> invoicePdf(@PathVariable Long id,
                                             @AuthenticationPrincipal UserDetails principal) {
        User user = userService.findByUsername(principal.getUsername());
        Booking booking = bookingService.findById(id);
        if (!booking.getUser().getId().equals(user.getId())) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Not your booking");
        }
        Invoice invoice = bookingService.invoiceFor(id);
        byte[] pdf = pdfService.render("pdf/invoice", Map.of(
                "booking", booking, "invoice", invoice,
                "orgName", configService.getOrganization().getName()));
        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_PDF)
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "inline; filename=\"" + invoice.getInvoiceNumber() + ".pdf\"")
                .body(pdf);
    }
}
