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

import de.effigenix.shared.common.RepositoryError;
import de.effigenix.domain.usermanagement.User;
import de.effigenix.domain.usermanagement.UserId;
import de.effigenix.domain.usermanagement.UserRepository;
import de.effigenix.domain.usermanagement.UserStatus;
import de.effigenix.infrastructure.usermanagement.persistence.mapper.UserMapper;
import de.effigenix.shared.common.Result;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * JPA Adapter for UserRepository (Domain Interface).
 * Infrastructure Layer - implements the Domain's UserRepository interface.
 *
 * This is the Adapter pattern in Hexagonal Architecture:
 * - Domain defines the interface (UserRepository)
 * - Infrastructure implements it (JpaUserRepository)
 * - Uses Spring Data JPA (UserJpaRepository) internally
 * - Translates between Domain and JPA entities using UserMapper
 *
 * @Transactional ensures database consistency.
 */
@Repository
@Profile("!no-db")
@Transactional(readOnly = true)
public class JpaUserRepository implements UserRepository {

    private static final Logger logger = LoggerFactory.getLogger(JpaUserRepository.class);
    private final UserJpaRepository jpaRepository;
    private final UserMapper userMapper;

    public JpaUserRepository(UserJpaRepository jpaRepository, UserMapper userMapper) {
        this.jpaRepository = jpaRepository;
        this.userMapper = userMapper;
    }

    @Override
    public Result<RepositoryError, Optional<User>> findById(UserId id) {
        try {
            Optional<User> result = jpaRepository.findById(id.value())
                    .map(userMapper::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<User>> findByUsername(String username) {
        try {
            Optional<User> result = jpaRepository.findByUsername(username)
                    .map(userMapper::toDomain);
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findByUsername", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, Optional<User>> findByEmail(String email) {
        try {
            Optional<User> result = jpaRepository.findByEmail(email)
                    .map(userMapper::toDomain);
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findByEmail", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, List<User>> findByBranchId(String branchId) {
        try {
            List<User> result = jpaRepository.findByBranchId(branchId).stream()
                    .map(userMapper::toDomain)
                    .collect(Collectors.toList());
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findByBranchId", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, List<User>> findByStatus(UserStatus status) {
        try {
            List<User> result = jpaRepository.findByStatus(status).stream()
                    .map(userMapper::toDomain)
                    .collect(Collectors.toList());
            return Result.success(result);
        } catch (Exception e) {
            logger.trace("Database error in findByStatus", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

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

    @Override
    @Transactional
    public Result<RepositoryError, Void> save(User user) {
        try {
            jpaRepository.save(userMapper.toEntity(user));
            return Result.success(null);
        } catch (Exception e) {
            logger.trace("Database error in save", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    @Transactional
    public Result<RepositoryError, Void> delete(User user) {
        try {
            jpaRepository.deleteById(user.id().value());
            return Result.success(null);
        } catch (Exception e) {
            logger.trace("Database error in delete", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, Boolean> existsByUsername(String username) {
        try {
            return Result.success(jpaRepository.existsByUsername(username));
        } catch (Exception e) {
            logger.trace("Database error in existsByUsername", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }

    @Override
    public Result<RepositoryError, Boolean> existsByEmail(String email) {
        try {
            return Result.success(jpaRepository.existsByEmail(email));
        } catch (Exception e) {
            logger.trace("Database error in existsByEmail", e);
            return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
        }
    }
}
