package com.social.media.infrastructure.persistence.repository;

import com.social.media.domain.user.aggregate.User;
import com.social.media.domain.user.repository.UserRepository;
import com.social.media.domain.user.valueobject.*;
import com.social.media.domain.company.valueobject.CompanyId;
import com.social.media.domain.shared.valueobject.Email;
import com.social.media.domain.shared.enums.UserType;
import com.social.media.domain.shared.enums.UserStatus;
import com.social.media.infrastructure.persistence.entity.UserEntity;
import com.social.media.infrastructure.persistence.entity.UserStatusEntity;
import com.social.media.infrastructure.persistence.entity.UserTypeEntity;
import com.social.media.infrastructure.persistence.mapper.UserEntityMapper;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Implementation of UserRepository using JPA
 */
@Repository
public class UserRepositoryImpl implements UserRepository {
    
    private final UserJpaRepository userJpaRepository;
    private final UserEntityMapper userEntityMapper;
    
    public UserRepositoryImpl(UserJpaRepository userJpaRepository, UserEntityMapper userEntityMapper) {
        this.userJpaRepository = userJpaRepository;
        this.userEntityMapper = userEntityMapper;
    }
    
    private Long getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.getPrincipal() instanceof String) {
            return Long.valueOf((String) authentication.getPrincipal());
        }
        throw new RuntimeException("No authenticated user found");
    }
    
    private Long getCurrentUserCompanyId() {
        Long userId = getCurrentUserId();
        return userJpaRepository.findById(userId)
            .map(user -> user.getCompanyId())
            .orElseThrow(() -> new RuntimeException("User not found or no company associated"));
    }
    
    @Override
    public User save(User user) {
        UserEntity entity = userEntityMapper.toEntity(user);
        UserEntity savedEntity = userJpaRepository.save(entity);
        return userEntityMapper.toDomain(savedEntity);
    }
    
    @Override
    public Optional<User> findById(UserId id) {
        return userJpaRepository.findById(id.value())
            .map(userEntityMapper::toDomain);
    }
    
    @Override
    public Optional<User> findByEmail(Email email) {
        // For authentication, use global email search without company restriction
        return userJpaRepository.findByEmailForAuthentication(email.value())
            .map(userEntityMapper::toDomain);
    }
    
    @Override
    public Optional<User> findByCpf(Cpf cpf) {
        Long companyId = getCurrentUserCompanyId();
        return userJpaRepository.findByCpfAndCompanyId(cpf.value(), companyId)
            .map(userEntityMapper::toDomain);
    }
    
    @Override
    public boolean existsByEmail(Email email) {
        Long companyId = getCurrentUserCompanyId();
        return userJpaRepository.existsByEmailAndCompanyId(email.value(), companyId);
    }
    
    @Override
    public boolean existsByCpf(Cpf cpf) {
        Long companyId = getCurrentUserCompanyId();
        return userJpaRepository.existsByCpfAndCompanyId(cpf.value(), companyId);
    }
    
    @Override
    public List<User> findByCompany(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return userJpaRepository.findByCompanyIdAndRequestingUserId(companyId.value(), userId, pageable)
            .stream()
            .map(userEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<User> findByStatus(UserStatus status) {
        Long companyId = getCurrentUserCompanyId();
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return userJpaRepository.findByCompanyIdAndStatusAndRequestingUserId(companyId, mapToEntityStatus(status), userId, pageable)
            .stream()
            .map(userEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<User> findByType(UserType type) {
        Long companyId = getCurrentUserCompanyId();
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return userJpaRepository.findByCompanyIdAndUserTypeAndRequestingUserId(companyId, mapToEntityType(type), userId, pageable)
            .stream()
            .map(userEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<User> findByCompanyAndStatus(CompanyId companyId, UserStatus status) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return userJpaRepository.findByCompanyIdAndStatusAndRequestingUserId(companyId.value(), mapToEntityStatus(status), userId, pageable)
            .stream()
            .map(userEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<User> findByCompanyAndType(CompanyId companyId, UserType type) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return userJpaRepository.findByCompanyIdAndUserTypeAndRequestingUserId(companyId.value(), mapToEntityType(type), userId, pageable)
            .stream()
            .map(userEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<User> findActiveUsers() {
        Long companyId = getCurrentUserCompanyId();
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return userJpaRepository.findByCompanyIdAndDeletedAndRequestingUserId(companyId, false, userId, pageable)
            .stream()
            .map(userEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<User> findByParentUser(UserId parentUserId) {
        // Note: This would require adding parentUserId field to UserEntity
        // For now, return empty list as this field is not implemented in entity
        return List.of();
    }
    
    @Override
    public Optional<User> findByVerificationToken(String token) {
        // TODO: Implement when email verification token field is added to database
        // For now, return empty as the field doesn't exist in the current schema
        return Optional.empty();
    }
    
    @Override
    public Optional<User> findByPasswordResetToken(String token) {
        // TODO: Implement when password reset token field is added to database
        // For now, return empty as the field doesn't exist in the current schema
        return Optional.empty();
    }
    
    @Override
    public void delete(UserId id) {
        userJpaRepository.deleteById(id.value());
    }
    
    @Override
    public long countUsers() {
        return userJpaRepository.count();
    }
    
    @Override
    public long countActiveUsers() {
        Long companyId = getCurrentUserCompanyId();
        Long userId = getCurrentUserId();
        return userJpaRepository.countByCompanyIdAndDeletedAndRequestingUserId(companyId, false, userId);
    }
    
    @Override
    public long countByCompany(CompanyId companyId) {
        Long userId = getCurrentUserId();
        return userJpaRepository.countByCompanyIdAndRequestingUserId(companyId.value(), userId);
    }
    
    @Override
    public long countByType(UserType type) {
        Long companyId = getCurrentUserCompanyId();
        Long userId = getCurrentUserId();
        return userJpaRepository.countByCompanyIdAndUserTypeAndRequestingUserId(companyId, mapToEntityType(type), userId);
    }
    
    @Override
    public boolean existsSuperAdmin() {
        return userJpaRepository.existsSuperAdminGlobally();
    }
    
    @Override
    public Optional<User> findSuperAdmin() {
        return userJpaRepository.findSuperAdminGlobally()
            .map(userEntityMapper::toDomain);
    }
    
    @Override
    public boolean existsByTypeAndCompany(UserType type, CompanyId companyId) {
        Long userId = getCurrentUserId();
        return userJpaRepository.existsByCompanyIdAndUserTypeAndRequestingUserId(companyId.value(), mapToEntityType(type), userId);
    }
    
    /**
     * Find all non-deleted users
     */
    public List<User> findAllActive() {
        Long companyId = getCurrentUserCompanyId();
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return userJpaRepository.findByCompanyIdAndDeletedAndRequestingUserId(companyId, false, userId, pageable)
            .stream()
            .map(userEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    /**
     * Find non-deleted users by company
     */
    public List<User> findActiveByCompany(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return userJpaRepository.findByCompanyIdAndDeletedAndRequestingUserId(companyId.value(), false, userId, pageable)
            .stream()
            .map(userEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    // Helper methods to convert enums
    private com.social.media.infrastructure.persistence.entity.UserStatusEntity mapToEntityStatus(UserStatus status) {
        return switch (status) {
            case ACTIVE -> com.social.media.infrastructure.persistence.entity.UserStatusEntity.ACTIVE;
            case INACTIVE -> com.social.media.infrastructure.persistence.entity.UserStatusEntity.INACTIVE;
            case SUSPENDED -> com.social.media.infrastructure.persistence.entity.UserStatusEntity.SUSPENDED;
            case PENDING -> com.social.media.infrastructure.persistence.entity.UserStatusEntity.PENDING;
            case BLOCKED -> com.social.media.infrastructure.persistence.entity.UserStatusEntity.BLOCKED;
        };
    }
    
    private com.social.media.infrastructure.persistence.entity.UserTypeEntity mapToEntityType(UserType type) {
        return switch (type) {
            case SUPER_ADMIN -> com.social.media.infrastructure.persistence.entity.UserTypeEntity.SUPER_ADMIN;
            case ADMIN -> com.social.media.infrastructure.persistence.entity.UserTypeEntity.ADMIN;
            case MANAGER -> com.social.media.infrastructure.persistence.entity.UserTypeEntity.MANAGER;
            case USER -> com.social.media.infrastructure.persistence.entity.UserTypeEntity.USER;
            case VIEWER -> com.social.media.infrastructure.persistence.entity.UserTypeEntity.VIEWER;
        };
    }
}
