package de.avatic.lcc.service.access;

import de.avatic.lcc.dto.configuration.countries.view.CountryDetailDTO;
import de.avatic.lcc.dto.generic.CountryDTO;
import de.avatic.lcc.model.db.country.Country;
import de.avatic.lcc.model.db.country.IsoCode;
import de.avatic.lcc.model.db.properties.CountryPropertyMappingId;
import de.avatic.lcc.model.db.rates.ValidityPeriodState;
import de.avatic.lcc.repositories.country.CountryPropertyRepository;
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.properties.PropertySetRepository;
import de.avatic.lcc.service.transformer.generic.CountryTransformer;
import de.avatic.lcc.util.exception.badrequest.NotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

/**
 * Service class for managing country-related data and operations.
 * Provides methods for setting properties, listing countries, and retrieving country details.
 */
@Service
public class CountryService {

    private final CountryRepository countryRepository;
    private final CountryTransformer countryTransformer;
    private final CountryPropertyRepository countryPropertyRepository;
    private final PropertySetRepository propertySetRepository;
    private final PropertyValidationService propertyValidationService;

    public CountryService(CountryRepository countryRepository, CountryTransformer countryTransformer, CountryPropertyRepository countryPropertyRepository, PropertySetRepository propertySetRepository, PropertyValidationService propertyValidationService) {
        this.countryRepository = countryRepository;
        this.countryTransformer = countryTransformer;
        this.countryPropertyRepository = countryPropertyRepository;
        this.propertySetRepository = propertySetRepository;
        this.propertyValidationService = propertyValidationService;
    }

    public static boolean isNumeric(String str) {
        if (str == null || str.isEmpty()) {
            return false;
        }
        try {
            Integer.parseInt(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * Sets a property value for a country identified by its ISO code.
     *
     * @param isoCode           The ISO code identifying the country.
     * @param countryPropertyId The identifier for the property to be set.
     * @param value             The value of the property to be set.
     *                          <p>
     *                          This is a transactional operation.
     */
    @Transactional
    public void setProperties(IsoCode isoCode, String countryPropertyId, String value) {

        try {
            var mappingId = CountryPropertyMappingId.valueOf(countryPropertyId.toUpperCase());

            var country = countryRepository.getByIsoCode(isoCode);

            if (country.isEmpty())
                throw new NotFoundException(NotFoundException.NotFoundType.COUNTRY, isoCode.getCode());

            propertyValidationService.validateCountryProperty(country.orElseThrow().getId(), mappingId, value);
            countryPropertyRepository.setProperty(propertySetRepository.getDraftSetId(), country.orElseThrow().getId(), mappingId.name(), value);

        } catch (IllegalArgumentException e) {
            throw new NotFoundException(NotFoundException.NotFoundType.COUNTRY_PROPERTY, "id", countryPropertyId, e);
        }
    }

    /**
     * Retrieves a paginated list of countries filtered by the specified criteria.
     *
     * @param filter The filter criteria for the countries.
     * @param page   The page number for pagination.
     * @param limit  The number of countries per page.
     * @return A paginated result containing country DTOs that match the filter criteria.
     * <p>
     * This is a transactional operation.
     */
    @Transactional
    public SearchQueryResult<CountryDTO> listCountries(Optional<String> filter, int page, int limit) {
        return SearchQueryResult.map(countryRepository.listCountries(filter, true, new SearchQueryPagination(page, limit)), countryTransformer::toCountryDTO);
    }

    /**
     * Retrieves a complete list of countries filtered by the specified criteria.
     *
     * @param filter The filter criteria for the countries.
     * @return A result containing country DTOs that match the filter criteria.
     * <p>
     * This is a transactional operation.
     */
    @Transactional
    public SearchQueryResult<CountryDTO> listCountries(Optional<String> filter) {
        return SearchQueryResult.map(countryRepository.listCountries(filter, true), countryTransformer::toCountryDTO);
    }

    /**
     * Retrieves detailed information about a specific country, including its properties.
     *
     * @param id            The unique identifier of the country.
     * @param propertySetId The property set ID for filtering associated properties.
     *                      If set to 0, retrieves all properties for the country.
     * @return An optional containing the detailed information of the country, or empty if not found.
     * <p>
     * This is a transactional operation.
     */
    @Transactional
    public Optional<CountryDetailDTO> getCountry(String id, Integer propertySetId) {

        Optional<Country> country;

        if (isNumeric(id)) {
            country = countryRepository.getById(Integer.parseInt(id));
        } else {
            try {
                var isoCode = IsoCode.valueOf(id);
                country = countryRepository.getByIsoCode(isoCode);
            }
            catch (IllegalArgumentException e) {
                throw new NotFoundException(NotFoundException.NotFoundType.COUNTRY, id, e);
            }
        }

        country.orElseThrow(() -> new NotFoundException(NotFoundException.NotFoundType.COUNTRY, id));

        CountryDetailDTO dto = new CountryDetailDTO();
        Country entity = country.get();

        dto.setIsoCode(entity.getIsoCode().getCode());
        dto.setRegionCode(entity.getRegionCode().getCode());
        dto.setName(entity.getIsoCode().getFullName());
        dto.setId(entity.getId());

        var state = propertySetId != 0 ? propertySetRepository.getState(propertySetId) : null;

        if (0 == propertySetId || (state != ValidityPeriodState.EXPIRED && state != ValidityPeriodState.INVALID)) {
            var props = countryPropertyRepository.listPropertiesByCountryId(entity.getId());

            if (props.isEmpty())
                throw new NotFoundException(NotFoundException.NotFoundType.COUNTRY_PROPERTY, "id", entity.getId().toString(), null);

            dto.setProperties(props);
        } else {
            var props = countryPropertyRepository.listPropertiesByCountryIdAndPropertySetId(entity.getId(), propertySetId);

            if (props.isEmpty())
                throw new NotFoundException(NotFoundException.NotFoundType.COUNTRY_PROPERTY, "property set id", propertySetId.toString(), null);

            dto.setProperties(props);
        }


        return Optional.of(dto);
    }


}
