package de.effigenix.infrastructure.production.persistence.mapper;

import de.effigenix.domain.masterdata.ArticleId;
import de.effigenix.domain.production.*;
import de.effigenix.infrastructure.production.persistence.entity.BatchEntity;
import de.effigenix.infrastructure.production.persistence.entity.ConsumptionEntity;
import de.effigenix.shared.common.Quantity;
import de.effigenix.shared.common.UnitOfMeasure;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class BatchMapper {

    public BatchEntity toEntity(Batch batch) {
        var entity = new BatchEntity(
                batch.id().value(),
                batch.batchNumber().value(),
                batch.recipeId().value(),
                batch.status().name(),
                batch.plannedQuantity().amount(),
                batch.plannedQuantity().uom().name(),
                batch.productionDate(),
                batch.bestBeforeDate(),
                batch.createdAt(),
                batch.updatedAt()
        );

        if (batch.actualQuantity() != null) {
            entity.setActualQuantityAmount(batch.actualQuantity().amount());
            entity.setActualQuantityUnit(batch.actualQuantity().uom().name());
        }
        if (batch.waste() != null) {
            entity.setWasteAmount(batch.waste().amount());
            entity.setWasteUnit(batch.waste().uom().name());
        }
        entity.setRemarks(batch.remarks());
        entity.setCompletedAt(batch.completedAt());
        entity.setCancellationReason(batch.cancellationReason());
        entity.setCancelledAt(batch.cancelledAt());

        for (Consumption c : batch.consumptions()) {
            entity.getConsumptions().add(toConsumptionEntity(c, entity));
        }

        return entity;
    }

    public void updateEntity(BatchEntity entity, Batch batch) {
        entity.setStatus(batch.status().name());
        entity.setUpdatedAt(batch.updatedAt());

        if (batch.actualQuantity() != null) {
            entity.setActualQuantityAmount(batch.actualQuantity().amount());
            entity.setActualQuantityUnit(batch.actualQuantity().uom().name());
        } else {
            entity.setActualQuantityAmount(null);
            entity.setActualQuantityUnit(null);
        }
        if (batch.waste() != null) {
            entity.setWasteAmount(batch.waste().amount());
            entity.setWasteUnit(batch.waste().uom().name());
        } else {
            entity.setWasteAmount(null);
            entity.setWasteUnit(null);
        }
        entity.setRemarks(batch.remarks());
        entity.setCompletedAt(batch.completedAt());
        entity.setCancellationReason(batch.cancellationReason());
        entity.setCancelledAt(batch.cancelledAt());

        Set<String> existingIds = entity.getConsumptions().stream()
                .map(ConsumptionEntity::getId)
                .collect(Collectors.toSet());

        for (Consumption c : batch.consumptions()) {
            if (!existingIds.contains(c.id().value())) {
                entity.getConsumptions().add(toConsumptionEntity(c, entity));
            }
        }
    }

    public Batch toDomain(BatchEntity entity) {
        List<Consumption> consumptions = entity.getConsumptions().stream()
                .map(ce -> Consumption.reconstitute(
                        ConsumptionId.of(ce.getId()),
                        BatchId.of(ce.getInputBatchId()),
                        ArticleId.of(ce.getArticleId()),
                        Quantity.reconstitute(
                                ce.getQuantityUsedAmount(),
                                UnitOfMeasure.valueOf(ce.getQuantityUsedUnit())
                        ),
                        ce.getConsumedAt()
                ))
                .toList();

        Quantity actualQuantity = entity.getActualQuantityAmount() != null
                ? Quantity.reconstitute(entity.getActualQuantityAmount(), UnitOfMeasure.valueOf(entity.getActualQuantityUnit()))
                : null;
        Quantity waste = entity.getWasteAmount() != null
                ? Quantity.reconstitute(entity.getWasteAmount(), UnitOfMeasure.valueOf(entity.getWasteUnit()))
                : null;

        return Batch.reconstitute(
                BatchId.of(entity.getId()),
                new BatchNumber(entity.getBatchNumber()),
                RecipeId.of(entity.getRecipeId()),
                BatchStatus.valueOf(entity.getStatus()),
                Quantity.reconstitute(
                        entity.getPlannedQuantityAmount(),
                        UnitOfMeasure.valueOf(entity.getPlannedQuantityUnit())
                ),
                actualQuantity,
                waste,
                entity.getRemarks(),
                entity.getProductionDate(),
                entity.getBestBeforeDate(),
                entity.getCreatedAt(),
                entity.getUpdatedAt(),
                entity.getCompletedAt(),
                entity.getCancellationReason(),
                entity.getCancelledAt(),
                entity.getVersion(),
                consumptions
        );
    }

    public Batch toDomainSummary(BatchEntity entity) {
        Quantity actualQuantity = entity.getActualQuantityAmount() != null
                ? Quantity.reconstitute(entity.getActualQuantityAmount(), UnitOfMeasure.valueOf(entity.getActualQuantityUnit()))
                : null;
        Quantity waste = entity.getWasteAmount() != null
                ? Quantity.reconstitute(entity.getWasteAmount(), UnitOfMeasure.valueOf(entity.getWasteUnit()))
                : null;

        return Batch.reconstitute(
                BatchId.of(entity.getId()),
                new BatchNumber(entity.getBatchNumber()),
                RecipeId.of(entity.getRecipeId()),
                BatchStatus.valueOf(entity.getStatus()),
                Quantity.reconstitute(
                        entity.getPlannedQuantityAmount(),
                        UnitOfMeasure.valueOf(entity.getPlannedQuantityUnit())
                ),
                actualQuantity,
                waste,
                entity.getRemarks(),
                entity.getProductionDate(),
                entity.getBestBeforeDate(),
                entity.getCreatedAt(),
                entity.getUpdatedAt(),
                entity.getCompletedAt(),
                entity.getCancellationReason(),
                entity.getCancelledAt(),
                entity.getVersion(),
                List.of()
        );
    }

    private ConsumptionEntity toConsumptionEntity(Consumption c, BatchEntity parent) {
        return new ConsumptionEntity(
                c.id().value(),
                parent,
                c.inputBatchId().value(),
                c.articleId().value(),
                c.quantityUsed().amount(),
                c.quantityUsed().uom().name(),
                c.consumedAt()
        );
    }
}
