package de.effigenix.infrastructure.inventory.persistence.repository;

import de.effigenix.domain.inventory.*;
import de.effigenix.domain.masterdata.ArticleId;
import de.effigenix.infrastructure.inventory.persistence.mapper.StockMapper;
import de.effigenix.shared.common.RepositoryError;
import de.effigenix.shared.common.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

@Repository
@Profile("!no-db")
@Transactional(readOnly = true)
public class JpaStockRepository implements StockRepository {

    private static final Logger logger = LoggerFactory.getLogger(JpaStockRepository.class);

    private final StockJpaRepository jpaRepository;
    private final StockMapper mapper;

    public JpaStockRepository(StockJpaRepository jpaRepository, StockMapper mapper) {
        this.jpaRepository = jpaRepository;
        this.mapper = mapper;
    }

    @Override
    public Result<RepositoryError, Optional<Stock>> findById(StockId id) {
        try {
            Optional<Stock> result = jpaRepository.findById(id.value())
                    .map(mapper::toDomain);
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findById", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, Optional<Stock>> findByArticleIdAndStorageLocationId(ArticleId articleId, StorageLocationId storageLocationId) {
        try {
            Optional<Stock> result = jpaRepository.findByArticleIdAndStorageLocationId(articleId.value(), storageLocationId.value())
                    .map(mapper::toDomain);
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findByArticleIdAndStorageLocationId", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, Boolean> existsByArticleIdAndStorageLocationId(ArticleId articleId, StorageLocationId storageLocationId) {
        try {
            return Result.success(jpaRepository.existsByArticleIdAndStorageLocationId(articleId.value(), storageLocationId.value()));
        } catch (Exception e) {
            logger.trace("Database error in existsByArticleIdAndStorageLocationId", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, List<Stock>> findAll() {
        try {
            List<Stock> result = jpaRepository.findAll().stream()
                    .map(mapper::toDomain)
                    .toList();
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findAll", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, List<Stock>> findAllByStorageLocationId(StorageLocationId storageLocationId) {
        try {
            List<Stock> result = jpaRepository.findAllByStorageLocationId(storageLocationId.value()).stream()
                    .map(mapper::toDomain)
                    .toList();
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findAllByStorageLocationId", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, List<Stock>> findAllByArticleId(ArticleId articleId) {
        try {
            List<Stock> result = jpaRepository.findAllByArticleId(articleId.value()).stream()
                    .map(mapper::toDomain)
                    .toList();
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findAllByArticleId", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, List<Stock>> findAllWithExpiryRelevantBatches(LocalDate referenceDate) {
        try {
            List<Stock> result = jpaRepository.findAllWithExpiryRelevantBatches(referenceDate).stream()
                    .map(mapper::toDomain)
                    .toList();
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findAllWithExpiryRelevantBatches", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, List<Stock>> findAllBelowMinimumLevel() {
        try {
            List<Stock> result = jpaRepository.findAllBelowMinimumLevel().stream()
                    .map(mapper::toDomain)
                    .toList();
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findAllBelowMinimumLevel", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    @Transactional
    public Result<RepositoryError, Void> save(Stock stock) {
        try {
            jpaRepository.save(mapper.toEntity(stock));
            return Result.success(null);
        } catch (DataIntegrityViolationException e) {
            logger.trace("Duplicate entry in save", e);
            return Result.failure(new RepositoryError.DuplicateEntry(
                    "Stock already exists for article " + stock.articleId().value()
                            + " at location " + stock.storageLocationId().value()));
        } catch (Exception e) {
            logger.trace("Database error in save", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }
}
