package com.mvrent.carrental.service;

import com.mvrent.carrental.model.*;
import com.mvrent.carrental.repository.BookingRepository;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Service
@RequiredArgsConstructor
public class ReportService {

    private final BookingRepository bookingRepository;
    private final VendorService vendorService;
    private final DriverService driverService;

    /* ---------- Bookings report ---------- */

    public List<Booking> bookings(BookingFilter f) {
        return bookingRepository.findAll(spec(f), Sort.by(Sort.Direction.DESC, "pickupDate"));
    }

    private Specification<Booking> spec(BookingFilter f) {
        return (root, query, cb) -> {
            List<Predicate> ps = new ArrayList<>();
            if (f.from() != null) ps.add(cb.greaterThanOrEqualTo(root.get("pickupDate"), f.from()));
            if (f.to() != null) ps.add(cb.lessThanOrEqualTo(root.get("pickupDate"), f.to()));
            if (f.carId() != null) ps.add(cb.equal(root.get("car").get("id"), f.carId()));
            if (f.driverId() != null) ps.add(cb.equal(root.get("driver").get("id"), f.driverId()));
            if (f.vendorId() != null) ps.add(cb.equal(root.get("vendor").get("id"), f.vendorId()));
            if (f.ownership() != null) ps.add(cb.equal(root.get("revenueOwner"), f.ownership()));
            if (f.status() != null) ps.add(cb.equal(root.get("status"), f.status()));
            if (f.customerCategoryId() != null) {
                ps.add(cb.equal(root.get("user").get("customerCategory").get("id"), f.customerCategoryId()));
            }
            return cb.and(ps.toArray(new Predicate[0]));
        };
    }

    public ReportSummary summarize(List<Booking> bookings) {
        BigDecimal gross = BigDecimal.ZERO, driver = BigDecimal.ZERO,
                tax = BigDecimal.ZERO, commission = BigDecimal.ZERO, total = BigDecimal.ZERO;
        for (Booking b : bookings) {
            gross = gross.add(nz(b.getVehicleCharge()));
            driver = driver.add(nz(b.getDriverCharge()));
            tax = tax.add(nz(b.getTaxAmount()));
            commission = commission.add(nz(b.getCommissionAmount()));
            total = total.add(nz(b.getTotalAmount()));
        }
        return new ReportSummary(bookings.size(), gross, driver, tax, commission, total);
    }

    /* ---------- Vendor-wise report ---------- */

    public List<VendorReportRow> vendorReport() {
        List<VendorReportRow> rows = new ArrayList<>();
        for (Vendor v : vendorService.findAll()) {
            VendorRevenue r = vendorService.revenue(v.getId());
            rows.add(new VendorReportRow(v.getName(), r.trips(), r.gross(), r.commission(), r.net()));
        }
        return rows;
    }

    /* ---------- Driver-wise report ---------- */

    public List<DriverReportRow> driverReport() {
        List<DriverReportRow> rows = new ArrayList<>();
        for (Driver d : driverService.findAll()) {
            List<Booking> trips = driverService.tripHistory(d);
            BigDecimal charges = BigDecimal.ZERO;
            for (Booking b : trips) charges = charges.add(nz(b.getDriverCharge()));
            rows.add(new DriverReportRow(d.getName(),
                    d.getCategory() != null ? d.getCategory().getName() : "—",
                    trips.size(), charges));
        }
        return rows;
    }

    private static BigDecimal nz(BigDecimal v) {
        return v == null ? BigDecimal.ZERO : v;
    }

    /* ---------- Row records ---------- */

    public record ReportSummary(long count, BigDecimal gross, BigDecimal driverCharges,
                                BigDecimal tax, BigDecimal commission, BigDecimal total) {}

    public record VendorReportRow(String vendor, long trips, BigDecimal gross,
                                  BigDecimal commission, BigDecimal net) {}

    public record DriverReportRow(String driver, String category, long trips, BigDecimal charges) {}
}
