package de.votian.services.service;

import de.votian.services.entity.*;
import de.votian.services.repository.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Service
@Transactional
public class CourierService {

    private final CourierRepository courierRepository;
    private final CompanyRepository companyRepository;
    private final UserRepository userRepository;
    private final AddressRepository addressRepository;
    private final CourierVehicleRepository courierVehicleRepository;

    public CourierService(CourierRepository courierRepository, CompanyRepository companyRepository,
                          UserRepository userRepository, AddressRepository addressRepository,
                          CourierVehicleRepository courierVehicleRepository) {
        this.courierRepository = courierRepository;
        this.companyRepository = companyRepository;
        this.userRepository = userRepository;
        this.addressRepository = addressRepository;
        this.courierVehicleRepository = courierVehicleRepository;
    }

    public Optional<Courier> findById(Long id) {
        return courierRepository.findById(Objects.requireNonNull(id));
    }

    public List<Courier> findByHeadquarters(Long hqId) {
        return courierRepository.findByHeadquartersId(hqId);
    }

    public List<Courier> search(Long hqId, String searchTerm) {
        return courierRepository.searchCouriers(hqId, searchTerm);
    }

    public boolean isSidUnique(String sid, Long hqId) {
        return courierRepository.findBySidAndHq(sid, hqId).isEmpty();
    }

    public boolean isEidUnique(String eid, Long hqId) {
        return courierRepository.findByEidAndHq(eid, hqId).isEmpty();
    }

    @Transactional
    public Courier createCourier(Courier courier, Company company, User user, Address address, String password) {
        if (hasAddressContent(address)) {
            Address savedAddress = addressRepository.save(Objects.requireNonNull(address));
            company.setAddressId(savedAddress.getId());
        }

        // Save company
        company.setType(3);
        company.setArchived("0");
        Company savedCompany = companyRepository.save(company);

        // Save user
        user.setType(3);
        user.setHeadquartersId(courier.getHeadquartersId());
        if (password != null && !password.isEmpty()) {
            user.setPassword(password);
            user.setPasswordModify(LocalDateTime.now());
        }
        User savedUser = userRepository.save(user);

        // Save courier
        courier.setCompanyId(savedCompany.getId());
        courier.setUserId(savedUser.getId());
        return courierRepository.save(courier);
    }

    @Transactional
    public Courier updateCourier(Long courierId, Courier updates, Company companyUpdates, User userUpdates, Address addressUpdates) {
        Courier courier = courierRepository.findById(Objects.requireNonNull(courierId))
                .orElseThrow(() -> new RuntimeException("Courier not found: " + courierId));

        // Update company
        companyRepository.findById(Objects.requireNonNull(courier.getCompanyId())).ifPresent(company -> {
            if (companyUpdates.getComp() != null) company.setComp(companyUpdates.getComp());
            if (companyUpdates.getComp2() != null) company.setComp2(companyUpdates.getComp2());
            if (companyUpdates.getHsno() != null) company.setHsno(companyUpdates.getHsno());
            if (companyUpdates.getIln() != null) company.setIln(companyUpdates.getIln());
            if (companyUpdates.getTaxIdNo() != null) company.setTaxIdNo(companyUpdates.getTaxIdNo());
            updateAddress(company, addressUpdates);
            companyRepository.save(Objects.requireNonNull(company));
        });

        // Update user
        userRepository.findById(Objects.requireNonNull(courier.getUserId())).ifPresent(user -> {
            user.setAccount(userUpdates.getAccount());
            user.setName(userUpdates.getName());
            user.setFirstname(userUpdates.getFirstname());
            user.setEmail(userUpdates.getEmail());
            user.setPhone(userUpdates.getPhone());
            user.setPhone2(userUpdates.getPhone2());
            user.setFax(userUpdates.getFax());
            user.setBirthdate(userUpdates.getBirthdate());
            user.setCountry(userUpdates.getCountry());
            userRepository.save(user);
        });

        // Update courier
        if (updates.getEid() != null) courier.setEid(updates.getEid());
        if (updates.getSid() != null) courier.setSid(updates.getSid());
        if (updates.getVehicleTypeId() != null) courier.setVehicleTypeId(updates.getVehicleTypeId());
        if (updates.getAvailable() != null) courier.setAvailable(updates.getAvailable());
        if (updates.getMobilePda() != null) courier.setMobilePda(updates.getMobilePda());
        if (updates.getGroup() != null) courier.setGroup(updates.getGroup());
        if (updates.getFilter() != null) courier.setFilter(updates.getFilter());

        return courierRepository.save(courier);
    }

    public List<CourierVehicle> getVehicles(Long courierId) {
        return courierVehicleRepository.findByCourierId(courierId);
    }

    public CourierVehicle saveVehicle(CourierVehicle vehicle) {
        return courierVehicleRepository.save(Objects.requireNonNull(vehicle));
    }

    public CourierVehicle updateVehicle(Long courierId, Long vehicleId, CourierVehicle updates) {
        CourierVehicle vehicle = courierVehicleRepository.findById(Objects.requireNonNull(vehicleId))
                .orElseThrow(() -> new RuntimeException("Vehicle not found: " + vehicleId));

        if (!courierId.equals(vehicle.getCourierId())) {
            throw new RuntimeException("Vehicle does not belong to courier: " + courierId);
        }

        if (updates.getSid() != null) {
            vehicle.setSid(updates.getSid());
        }
        if (updates.getVehicleTypeId() != null) {
            vehicle.setVehicleTypeId(updates.getVehicleTypeId());
        }

        return courierVehicleRepository.save(vehicle);
    }

    public void deleteVehicle(Long courierId, Long vehicleId) {
        CourierVehicle vehicle = courierVehicleRepository.findById(Objects.requireNonNull(vehicleId))
                .orElseThrow(() -> new RuntimeException("Vehicle not found: " + vehicleId));

        if (!courierId.equals(vehicle.getCourierId())) {
            throw new RuntimeException("Vehicle does not belong to courier: " + courierId);
        }

        courierVehicleRepository.delete(vehicle);
    }

    private void updateAddress(Company company, Address addressUpdates) {
        if (addressUpdates == null) {
            return;
        }

        if (company.getAddressId() == null) {
            if (hasAddressContent(addressUpdates)) {
                Address savedAddress = addressRepository.save(addressUpdates);
                company.setAddressId(savedAddress.getId());
            }
            return;
        }

        addressRepository.findById(Objects.requireNonNull(company.getAddressId())).ifPresentOrElse(address -> {
            address.setStreet(addressUpdates.getStreet());
            address.setZipcode(addressUpdates.getZipcode());
            address.setCity(addressUpdates.getCity());
            address.setCountry(addressUpdates.getCountry());
            addressRepository.save(address);
        }, () -> {
            if (hasAddressContent(addressUpdates)) {
                Address savedAddress = addressRepository.save(addressUpdates);
                company.setAddressId(savedAddress.getId());
            }
        });
    }

    private boolean hasAddressContent(Address address) {
        return address != null && (
                hasText(address.getStreet()) ||
                hasText(address.getZipcode()) ||
                hasText(address.getCity()) ||
                hasText(address.getCountry())
        );
    }

    private boolean hasText(String value) {
        return value != null && !value.isBlank();
    }
}
