package de.avatic.lcc.service.report;

import de.avatic.lcc.dto.generic.NodeDTO;
import de.avatic.lcc.dto.report.ReportDTO;
import de.avatic.lcc.model.db.calculations.CalculationJob;
import de.avatic.lcc.repositories.NodeRepository;
import de.avatic.lcc.repositories.calculation.CalculationJobRepository;
import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
import de.avatic.lcc.repositories.users.UserNodeRepository;
import de.avatic.lcc.service.transformer.generic.NodeTransformer;
import de.avatic.lcc.service.transformer.report.ReportTransformer;
import org.springframework.stereotype.Service;

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

@Service
public class ReportingService {
    private final NodeRepository nodeRepository;
    private final NodeTransformer nodeTransformer;
    private final ValidityPeriodRepository validityPeriodRepository;
    private final CalculationJobRepository calculationJobRepository;
    private final UserNodeRepository userNodeRepository;

    private final ReportTransformer reportTransformer;

    public ReportingService(NodeRepository nodeRepository, NodeTransformer nodeTransformer, ValidityPeriodRepository validityPeriodRepository, CalculationJobRepository calculationJobRepository, UserNodeRepository userNodeRepository, ReportTransformer reportTransformer) {
        this.nodeRepository = nodeRepository;
        this.nodeTransformer = nodeTransformer;
        this.validityPeriodRepository = validityPeriodRepository;
        this.calculationJobRepository = calculationJobRepository;
        this.userNodeRepository = userNodeRepository;
        this.reportTransformer = reportTransformer;
    }

    public List<List<NodeDTO>> findSupplierForReporting(Integer materialId) {
        var periodIds = validityPeriodRepository.findValidityPeriodsWithReportByMaterialId(materialId);

        var nodes = new ArrayList<List<NodeDTO>>();
        for (var periodId : periodIds) {
            var periodNodes = new ArrayList<>(nodeRepository.findNodeListsForReportingByPeriodId(materialId, periodId).stream().map(nodeTransformer::toNodeDTO).toList());
            periodNodes.addAll(userNodeRepository.findNodeListsForReportingByPeriodId(materialId, periodId).stream().map(nodeTransformer::toUserNodeDTO).peek(n -> n.setUserNode(true)).toList());

            //TODO destinations have to match.

            nodes.add(periodNodes);
        }

        return nodes;
    }

    public List<ReportDTO> getReport(Integer materialId, List<Integer> nodeIds, List<Integer> userNodeIds) {
        var tuple = validityPeriodRepository.getValidPeriodForReportingByMaterialId(materialId, nodeIds, userNodeIds);

        if (tuple.isEmpty())
            throw new IllegalArgumentException("No valid period found");

        var periodId = tuple.get().periodId();
        var setId = tuple.get().propertySetId();

        var jobs = new ArrayList<CalculationJob>();

        if(!nodeIds.isEmpty())
            jobs.addAll(nodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJobWithJobStateValid(periodId, setId, nodeId, materialId)).filter(Optional::isPresent).map(Optional::get).toList());

        if(!userNodeIds.isEmpty())
            jobs.addAll(userNodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJobWithJobStateValidUserNodeId(periodId, setId,nodeId ,materialId)).filter(Optional::isPresent).map(Optional::get).toList());

        return jobs.stream().map(reportTransformer::toReportDTO).toList();
    }

}
