package de.avatic.lcc.service.access;

import de.avatic.lcc.dto.configuration.matrixrates.MatrixRateDTO;
import de.avatic.lcc.model.db.rates.MatrixRate;
import de.avatic.lcc.repositories.country.CountryRepository;
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import de.avatic.lcc.repositories.rates.MatrixRateRepository;
import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
import de.avatic.lcc.service.transformer.generic.CountryTransformer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * Service class for managing matrix rates including retrieval, listing, and transformations
 * of matrix rate data objects. Provides methods to interact with repositories and handle
 * business logic for matrix rates.
 */
@Service
public class  MatrixRateService {

    private final ValidityPeriodRepository validityPeriodRepository;
    private final MatrixRateRepository matrixRateRepository;
    private final CountryRepository countryRepository;
    private final CountryTransformer countryTransformer;

    public MatrixRateService(ValidityPeriodRepository validityPeriodRepository, MatrixRateRepository matrixRateRepository, CountryRepository countryRepository, CountryTransformer countryTransformer) {

        this.validityPeriodRepository = validityPeriodRepository;
        this.matrixRateRepository = matrixRateRepository;
        this.countryRepository = countryRepository;
        this.countryTransformer = countryTransformer;
    }

    /**
     * Retrieves a paginated list of matrix rates filtered by the specified validity date.
     *
     * @param limit   the maximum number of results to retrieve per page
     * @param page    the current page number to retrieve
     * @param validAt the date for which to find the validity period
     * @return a {@link SearchQueryResult} containing matrix rate DTOs with pagination information
     */
    @Transactional
    public SearchQueryResult<MatrixRateDTO> listRates(String filter, int limit, int page, LocalDateTime validAt) {
        Optional<Integer> periodId = validityPeriodRepository.getPeriodId(validAt);
        return listRates(filter, limit, page, periodId.orElseThrow());
    }

    /**
     * Retrieves a paginated list of matrix rates filtered by a specific validity period ID.
     *
     * @param limit    the maximum number of results to retrieve per page
     * @param page     the current page number to retrieve
     * @param periodId the ID of the validity period to filter the rates by
     * @return a {@link SearchQueryResult} containing matrix rate DTOs with pagination information
     */
    @Transactional
    public SearchQueryResult<MatrixRateDTO> listRates(String filter, int limit, int page, Integer periodId) {
        if (null == periodId)
            return listRates(filter,limit, page);

        return SearchQueryResult.map(matrixRateRepository.listRatesByPeriodId(filter, new SearchQueryPagination(page, limit), periodId), this::toMatrixRateDTO);
    }

    /**
     * Retrieves a paginated list of all matrix rates without applying filtering criteria.
     * It returns the data of the currently valid validity period.
     * @param limit the maximum number of results to retrieve per page
     * @param page  the current page number to retrieve
     * @return a {@link SearchQueryResult} containing matrix rate DTOs with pagination information
     */
    @Transactional
    public SearchQueryResult<MatrixRateDTO> listRates(String filter, int limit, int page) {
        Integer id = validityPeriodRepository.getValidPeriodId().orElseThrow(() -> new IllegalStateException("No valid period found that is VALID"));
        return SearchQueryResult.map(matrixRateRepository.listRatesByPeriodId(filter, new SearchQueryPagination(page, limit), id), this::toMatrixRateDTO);
    }

    /**
     * Retrieves a matrix rate by its unique identifier.
     *
     * @param id the unique identifier of the matrix rate
     * @return a {@link MatrixRateDTO} containing the matrix rate details
     */
    @Transactional
    public MatrixRateDTO getRate(Integer id) {
        return toMatrixRateDTO(matrixRateRepository.getById(id));
    }

    /**
     * Transforms a {@link MatrixRate} entity into a {@link MatrixRateDTO}.
     *
     * @param entity the {@link MatrixRate} entity to transform
     * @return a {@link MatrixRateDTO} containing the transformed data
     */
    private MatrixRateDTO toMatrixRateDTO(MatrixRate entity) {
        MatrixRateDTO dto = new MatrixRateDTO();

        dto.setId(entity.getId());
        dto.setSource(countryTransformer.toCountryDTO(countryRepository.getById(entity.getFromCountry()).orElseThrow()));
        dto.setDestination(countryTransformer.toCountryDTO(countryRepository.getById(entity.getToCountry()).orElseThrow()));
        dto.setRate(entity.getRate());

        return dto;
    }
}
