package de.avatic.lcc.service.access;

import de.avatic.lcc.dto.configuration.material.update.MaterialUpdateDTO;
import de.avatic.lcc.dto.configuration.material.view.MaterialDetailDTO;
import de.avatic.lcc.dto.generic.MaterialDTO;
import de.avatic.lcc.model.db.materials.Material;
import de.avatic.lcc.repositories.MaterialRepository;
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import de.avatic.lcc.service.transformer.generic.MaterialTransformer;
import de.avatic.lcc.service.transformer.material.MaterialDetailTransformer;
import de.avatic.lcc.service.transformer.material.MaterialUpdateDTOTransformer;
import de.avatic.lcc.util.exception.badrequest.MaterialNotFoundException;
import de.avatic.lcc.util.exception.badrequest.NotFoundException;
import jakarta.validation.constraints.Size;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

/**
 * Service class responsible for managing materials. Provides functionality to
 * list, retrieve, and manipulate material data. Acts as a bridge between the
 * repository and transformer layers.
 */
@Service
public class MaterialService {

    private final MaterialRepository materialRepository;
    private final MaterialTransformer materialTransformer;
    private final MaterialDetailTransformer materialDetailTransformer;
    private final MaterialUpdateDTOTransformer materialUpdateDTOTransformer;

    /**
     * Constructor for MaterialService.
     *
     * @param materialRepository        repository for accessing material data
     * @param materialTransformer       transformer to convert material entities to DTOs
     * @param materialDetailTransformer transformer to convert material entities to detail DTOs
     */
    public MaterialService(MaterialRepository materialRepository, MaterialTransformer materialTransformer, MaterialDetailTransformer materialDetailTransformer, MaterialUpdateDTOTransformer materialUpdateDTOTransformer) {
        this.materialRepository = materialRepository;
        this.materialTransformer = materialTransformer;
        this.materialDetailTransformer = materialDetailTransformer;
        this.materialUpdateDTOTransformer = materialUpdateDTOTransformer;
    }

    /**
     * Lists materials based on the provided criteria, including optional filtering,
     * pagination, and exclusion of deprecated entries.
     *
     * @param filter            an optional filter to apply to the material list, such as a search term
     * @param page              the page number for pagination
     * @param limit             the maximum number of materials to include in the result
     * @param excludeDeprecated a flag indicating whether to exclude deprecated materials
     * @return a {@code SearchQueryResult<MaterialDTO>} containing the paginated list of materials
     */
    public SearchQueryResult<MaterialDTO> listMaterial(Optional<String> filter, int page, int limit, boolean excludeDeprecated) {
        SearchQueryResult<Material> queryResult = materialRepository.listMaterials(filter, excludeDeprecated, new SearchQueryPagination(page, limit));
        return SearchQueryResult.map(queryResult, materialTransformer::toMaterialDTO);
    }

    /**
     * Retrieves the details of a specific material by its ID.
     *
     * @param id the unique identifier of the material
     * @return a {@link MaterialDetailDTO} representing the material details
     * @throws MaterialNotFoundException if no material with the given ID is found
     */
    public MaterialDetailDTO getMaterial(Integer id) {
        return materialDetailTransformer.toMaterialDetailDTO(materialRepository.getByIdIncludeDeprecated(id).orElseThrow(() -> new NotFoundException(NotFoundException.NotFoundType.MATERIAL, id.toString())));
    }

    public void updateMaterial(List<@Size(max = 100, min = 1) MaterialUpdateDTO> dtos) {
        var materials = dtos.stream().map(materialUpdateDTOTransformer::fromMaterialUpdateDTO).toList();
        var toBeDeleted = materials.stream().filter(m -> m.getDeprecated() != null && m.getDeprecated()).map(Material::getId).toList();
        var toBeUpdated = materials.stream().filter(m -> m.getDeprecated() != null && m.getDeprecated()).toList();

        if (!toBeDeleted.isEmpty())
            toBeDeleted.forEach(materialRepository::setDeprecatedById);

        if (!toBeUpdated.isEmpty())
            toBeUpdated.forEach(materialRepository::updateByPartNumber);

    }

    /**
     * Updates an existing material with new data.
     *
     * @param id  the ID of the material to update
     * @param dto the data transfer object containing the updated material details
     * @return the ID of the updated material
     * @throws MaterialNotFoundException if no material with the given ID is found
     */
/*    public Integer updateMaterial(Integer id, MaterialUpdateDTO dto) {
        return materialRepository.update(materialUpdateDTOTransformer.fromMaterialUpdateDTO(dto)).orElseThrow(() -> new MaterialNotFoundException(id));
    }*/

    /**
     * Creates a new material based on the provided data.
     *
     * @param dto the data transfer object containing the new material details
     * @return the ID of the newly created material
     * @throws RuntimeException if the material could not be created
     */
/*    public Integer createMaterial(MaterialUpdateDTO dto) {
        return materialRepository.create(materialUpdateDTOTransformer.fromMaterialUpdateDTO(dto)).orElseThrow(() -> new RuntimeException("Unable to create Material " + dto));
    }*/

    /**
     * Marks a material as deprecated by its ID.
     *
     * @param id the ID of the material to deprecate
     * @return the ID of the deprecated material
     * @throws MaterialNotFoundException if no material with the given ID is found
     */
/*    public Integer deleteMaterial(Integer id) {
        return materialRepository.setDeprecatedById(id).orElseThrow(() -> new MaterialNotFoundException(id));
    }*/
}
