package com.mvrent.carrental.service;

import com.mvrent.carrental.model.*;
import com.mvrent.carrental.repository.BookingRepository;
import com.mvrent.carrental.repository.VendorRepository;
import com.mvrent.carrental.repository.VendorSettlementRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;

@Service
@RequiredArgsConstructor
public class SettlementService {

    /** Only realised revenue (confirmed/completed) is settled. */
    static final List<BookingStatus> SETTLED_STATUSES =
            List.of(BookingStatus.CONFIRMED, BookingStatus.COMPLETED);

    private final VendorSettlementRepository settlementRepository;
    private final VendorRepository vendorRepository;
    private final BookingRepository bookingRepository;

    public List<VendorSettlement> findAll() {
        return settlementRepository.findAllByOrderByGeneratedAtDesc();
    }

    public List<VendorSettlement> findByVendor(Long vendorId) {
        return settlementRepository.findByVendorIdOrderByGeneratedAtDesc(vendorId);
    }

    public VendorSettlement findById(Long id) {
        return settlementRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Settlement not found: " + id));
    }

    /** Bookings included in a settlement (its vendor, period and settled statuses). */
    public List<Booking> tripsFor(VendorSettlement s) {
        return bookingRepository.findByCarVendorIdAndStatusInAndPickupDateBetweenOrderByPickupDate(
                s.getVendor().getId(), SETTLED_STATUSES, s.getPeriodStart(), s.getPeriodEnd());
    }

    @Transactional
    public VendorSettlement generate(Long vendorId, LocalDate start, LocalDate end) {
        if (start == null || end == null || end.isBefore(start)) {
            throw new IllegalArgumentException("Invalid settlement period");
        }
        Vendor vendor = vendorRepository.findById(vendorId)
                .orElseThrow(() -> new IllegalArgumentException("Vendor not found: " + vendorId));

        List<Booking> trips = bookingRepository
                .findByCarVendorIdAndStatusInAndPickupDateBetweenOrderByPickupDate(
                        vendorId, SETTLED_STATUSES, start, end);

        BigDecimal gross = BigDecimal.ZERO;
        BigDecimal commission = BigDecimal.ZERO;
        BigDecimal driverCharges = BigDecimal.ZERO;
        for (Booking b : trips) {
            gross = gross.add(nz(b.getVehicleCharge()));
            commission = commission.add(nz(b.getCommissionAmount()));
            // Driver charge is owed to the vendor only when the driver is vendor-supplied.
            if (b.getDriver() != null && b.getDriver().getVendor() != null
                    && b.getDriver().getVendor().getId().equals(vendorId)) {
                driverCharges = driverCharges.add(nz(b.getDriverCharge()));
            }
        }
        BigDecimal net = gross.subtract(commission).add(driverCharges);

        VendorSettlement settlement = VendorSettlement.builder()
                .vendor(vendor).periodStart(start).periodEnd(end)
                .tripCount(trips.size())
                .grossRevenue(gross).commissionAmount(commission)
                .driverCharges(driverCharges).netPayable(net)
                .status(SettlementStatus.DRAFT)
                .build();
        return settlementRepository.save(settlement);
    }

    @Transactional
    public void updateStatus(Long id, SettlementStatus status) {
        findById(id).setStatus(status);
    }

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