package com.mvrent.carrental.controller;

import com.mvrent.carrental.model.*;
import com.mvrent.carrental.service.CarService;
import com.mvrent.carrental.service.FileStorageService;
import com.mvrent.carrental.web.CarDto;
import com.mvrent.carrental.web.CarForm;
import com.mvrent.carrental.web.CarMediaDto;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/** JSON API backing the AJAX Manage Cars screen. */
@RestController
@RequestMapping("/admin/api/cars")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ADMIN') or hasAuthority('MANAGE_FLEET')")
public class AdminCarApiController {

    private final CarService carService;
    private final FileStorageService fileStorage;

    @GetMapping
    public List<CarDto> list() {
        return carService.findAll().stream().map(CarDto::from).toList();
    }

    @GetMapping("/{id}")
    public CarDto get(@PathVariable Long id) {
        return CarDto.from(carService.findById(id));
    }

    @PostMapping
    public ResponseEntity<?> save(@RequestBody CarForm f) {
        Map<String, String> errors = validate(f);
        if (!errors.isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("ok", false, "errors", errors));
        }

        Car car = new Car();
        if (f.id() != null) car.setId(f.id());
        car.setMake(trim(f.make()));
        car.setModel(trim(f.model()));
        car.setRegistrationNumber(trim(f.registrationNumber()));
        car.setYearOfRegistration(f.yearOfRegistration());
        car.setColour(trim(f.colour()));
        car.setType(f.type());
        car.setSeats(f.seats() == null ? 0 : f.seats());
        car.setTransmission(f.transmission());
        car.setFuelType(f.fuelType());
        car.setHourlyRate(f.hourlyRate());
        car.setPricePerDay(f.pricePerDay());
        car.setMonthlyRate(f.monthlyRate());
        car.setOwnership(f.ownership() == null ? CarOwnership.OWNED : f.ownership());
        car.setCommissionPercentOverride(f.commissionPercentOverride());
        car.setUnitsTotal(f.unitsTotal() == null ? 1 : f.unitsTotal());
        car.setStatus(f.status() == null ? CarStatus.ACTIVE : f.status());
        car.setImageUrl(trim(f.imageUrl()));
        car.setAvailable(f.available() == null || f.available());

        try {
            Car saved = carService.saveFromForm(car, parseTags(f.featureTagsCsv()), f.vendorId());
            return ResponseEntity.ok(Map.of("ok", true, "car", CarDto.from(saved)));
        } catch (DataIntegrityViolationException ex) {
            return ResponseEntity.badRequest().body(Map.of("ok", false,
                    "errors", Map.of("registrationNumber", "Registration number already exists")));
        }
    }

    @PostMapping("/{id}/delete")
    public ResponseEntity<?> delete(@PathVariable Long id) {
        try {
            carService.delete(id);
            return ResponseEntity.ok(Map.of("ok", true));
        } catch (DataIntegrityViolationException ex) {
            return ResponseEntity.badRequest().body(Map.of("ok", false,
                    "message", "Cannot delete — this car has bookings. Set it to RETIRED instead."));
        }
    }

    /* ---------- Documents & photos ---------- */

    @GetMapping("/{id}/media")
    public CarMediaDto media(@PathVariable Long id) {
        return CarMediaDto.from(carService.findById(id));
    }

    @PostMapping("/{id}/documents")
    public ResponseEntity<?> addDocument(@PathVariable Long id,
                                         @RequestParam CarDocumentType docType,
                                         @RequestParam(required = false) MultipartFile file,
                                         @RequestParam(required = false) String fileUrl,
                                         @RequestParam(required = false)
                                         @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate issuedDate,
                                         @RequestParam(required = false)
                                         @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate expiryDate) {
        String stored = fileStorage.store(file);
        String url = stored != null ? stored : fileUrl;
        if (url == null || url.isBlank()) {
            return ResponseEntity.badRequest().body(Map.of("ok", false, "message", "Upload a file or provide a URL"));
        }
        carService.addDocument(id, docType, url, issuedDate, expiryDate);
        return ResponseEntity.ok(Map.of("ok", true));
    }

    @PostMapping("/{id}/documents/{docId}/delete")
    public ResponseEntity<?> deleteDocument(@PathVariable Long id, @PathVariable Long docId) {
        carService.deleteDocument(docId);
        return ResponseEntity.ok(Map.of("ok", true));
    }

    @PostMapping("/{id}/photos")
    public ResponseEntity<?> addPhoto(@PathVariable Long id,
                                      @RequestParam(required = false) MultipartFile file,
                                      @RequestParam(required = false) String url,
                                      @RequestParam(defaultValue = "0") int sortOrder) {
        String stored = fileStorage.store(file);
        String finalUrl = stored != null ? stored : url;
        if (finalUrl == null || finalUrl.isBlank()) {
            return ResponseEntity.badRequest().body(Map.of("ok", false, "message", "Upload a photo or provide a URL"));
        }
        carService.addPhoto(id, finalUrl, sortOrder);
        return ResponseEntity.ok(Map.of("ok", true));
    }

    @PostMapping("/{id}/photos/{photoId}/delete")
    public ResponseEntity<?> deletePhoto(@PathVariable Long id, @PathVariable Long photoId) {
        carService.deletePhoto(photoId);
        return ResponseEntity.ok(Map.of("ok", true));
    }

    private Map<String, String> validate(CarForm f) {
        Map<String, String> e = new LinkedHashMap<>();
        if (isBlank(f.make())) e.put("make", "Make is required");
        if (isBlank(f.model())) e.put("model", "Model is required");
        if (isBlank(f.registrationNumber())) e.put("registrationNumber", "Registration number is required");
        if (f.type() == null) e.put("type", "Type is required");
        if (f.pricePerDay() == null || f.pricePerDay().compareTo(BigDecimal.ZERO) < 0)
            e.put("pricePerDay", "Valid daily rate is required");
        if (f.seats() != null && (f.seats() < 2 || f.seats() > 15))
            e.put("seats", "Seats must be 2–15");
        return e;
    }

    private Set<String> parseTags(String csv) {
        if (csv == null || csv.isBlank()) return new LinkedHashSet<>();
        return Arrays.stream(csv.split(","))
                .map(String::trim).filter(s -> !s.isEmpty())
                .collect(Collectors.toCollection(LinkedHashSet::new));
    }

    private static boolean isBlank(String s) { return s == null || s.isBlank(); }
    private static String trim(String s) { return s == null ? null : s.trim(); }
}
