package com.social.media.infrastructure.persistence.repository;

import com.social.media.domain.content.aggregate.Media;
import com.social.media.domain.content.repository.MediaRepository;
import com.social.media.domain.content.valueobject.MediaId;
import com.social.media.domain.content.valueobject.MediaType;
import com.social.media.domain.company.valueobject.CompanyId;
import com.social.media.domain.user.valueobject.UserId;
import com.social.media.infrastructure.persistence.entity.MediaEntity;
import com.social.media.infrastructure.persistence.entity.MediaTypeEntity;
import com.social.media.infrastructure.persistence.mapper.MediaEntityMapper;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Implementation of MediaRepository using JPA
 */
@Repository
public class MediaRepositoryImpl implements MediaRepository {
    
    private final MediaJpaRepository mediaJpaRepository;
    private final MediaEntityMapper mediaEntityMapper;
    
    public MediaRepositoryImpl(MediaJpaRepository mediaJpaRepository, MediaEntityMapper mediaEntityMapper) {
        this.mediaJpaRepository = mediaJpaRepository;
        this.mediaEntityMapper = mediaEntityMapper;
    }
    
    /**
     * Helper method to get current user ID from security context
     * TODO: Replace with proper authentication service integration
     */
    private Long getCurrentUserId() {
        // Temporary implementation - should be replaced with proper authentication service
        return 1L; // This should be replaced with actual user ID from security context
    }
    
    @Override
    public Media save(Media media) {
        MediaEntity entity = mediaEntityMapper.toEntity(media);
        MediaEntity savedEntity = mediaJpaRepository.save(entity);
        return mediaEntityMapper.toDomain(savedEntity);
    }
    
    @Override
    public Optional<Media> findById(MediaId id) {
        return mediaJpaRepository.findById(id.value())
            .map(mediaEntityMapper::toDomain);
    }
    
    @Override
    public List<Media> findByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return mediaJpaRepository.findByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(mediaEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Media> findByCompanyIdAndUploadedBy(CompanyId companyId, UserId uploadedBy) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return mediaJpaRepository.findByCompanyIdAndUploadedByAndUserId(companyId.value(), uploadedBy.value(), userId, pageable)
            .stream()
            .map(mediaEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Media> findByCompanyIdAndMediaType(CompanyId companyId, MediaType mediaType) {
        MediaTypeEntity entityType = mapToEntityMediaType(mediaType);
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return mediaJpaRepository.findByCompanyIdAndMediaTypeAndUserId(companyId.value(), entityType, userId, pageable)
            .stream()
            .map(mediaEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Media> findByCompanyIdAndIsPublic(CompanyId companyId, boolean isPublic) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return mediaJpaRepository.findByCompanyIdAndIsPublicAndUserId(companyId.value(), isPublic, userId, pageable)
            .stream()
            .map(mediaEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Media> findByCompanyIdAndUploadedAfter(CompanyId companyId, LocalDateTime uploadedAfter) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return mediaJpaRepository.findByCompanyIdAndUploadedAtAfterAndUserId(companyId.value(), uploadedAfter, userId, pageable)
            .stream()
            .map(mediaEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Media> findByCompanyIdAndUploadedBetween(CompanyId companyId, LocalDateTime start, LocalDateTime end) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return mediaJpaRepository.findByCompanyIdAndUploadedAtBetweenAndUserId(companyId.value(), start, end, userId, pageable)
            .stream()
            .map(mediaEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Media> findByIds(List<MediaId> mediaIds) {
        List<Long> idValues = mediaIds.stream().map(MediaId::value).collect(Collectors.toList());
        // Need to get company ID somehow - this is a limitation of the current interface
        // For now, we'll use a temporary approach
        Long userId = getCurrentUserId();
        Long companyId = 1L; // TODO: Get actual company ID from security context or add to method signature
        return mediaJpaRepository.findByIdInAndCompanyIdAndUserId(idValues, companyId, userId)
            .stream()
            .map(mediaEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public boolean existsByStoredFileName(String storedFileName) {
        // Need company ID for security - temporarily using a default
        Long companyId = 1L; // TODO: Get actual company ID from security context
        return mediaJpaRepository.existsByStoredFileNameAndCompanyId(storedFileName, companyId);
    }
    
    @Override
    public void delete(Media media) {
        mediaJpaRepository.deleteById(media.getId().value());
    }
    
    @Override
    public void deleteById(MediaId id) {
        mediaJpaRepository.deleteById(id.value());
    }
    
    @Override
    public long countByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        return mediaJpaRepository.countByCompanyIdAndUserId(companyId.value(), userId);
    }
    
    @Override
    public long getTotalFileSizeByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Long totalSize = mediaJpaRepository.getTotalFileSizeByCompanyIdAndUserId(companyId.value(), userId);
        return totalSize != null ? totalSize : 0L;
    }
    
    @Override
    public List<Media> findUnusedMediaByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return mediaJpaRepository.findUnusedMediaByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(mediaEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    // Helper method for enum conversion
    private MediaTypeEntity mapToEntityMediaType(MediaType mediaType) {
        return switch (mediaType) {
            case IMAGE -> MediaTypeEntity.IMAGE;
            case VIDEO -> MediaTypeEntity.VIDEO;
            case AUDIO -> MediaTypeEntity.AUDIO;
            case DOCUMENT -> MediaTypeEntity.DOCUMENT;
            case GIF -> MediaTypeEntity.GIF;
        };
    }
}
