package com.social.media.infrastructure.persistence.repository;

import com.social.media.domain.content.aggregate.Post;
import com.social.media.domain.content.repository.PostRepository;
import com.social.media.domain.content.valueobject.*;
import com.social.media.domain.company.valueobject.CompanyId;
import com.social.media.domain.user.valueobject.UserId;
import com.social.media.domain.socialaccount.valueobject.SocialAccountId;
import com.social.media.infrastructure.persistence.entity.PostEntity;
import com.social.media.infrastructure.persistence.entity.PostStatusEntity;
import com.social.media.infrastructure.persistence.entity.PostTypeEntity;
import com.social.media.infrastructure.persistence.mapper.PostEntityMapper;
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 PostRepository using JPA
 */
@Repository
public class PostRepositoryImpl implements PostRepository {
    
    private final PostJpaRepository postJpaRepository;
    private final PostEntityMapper postEntityMapper;
    
    public PostRepositoryImpl(PostJpaRepository postJpaRepository, PostEntityMapper postEntityMapper) {
        this.postJpaRepository = postJpaRepository;
        this.postEntityMapper = postEntityMapper;
    }
    
    /**
     * 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 Post save(Post post) {
        PostEntity entity = postEntityMapper.toEntity(post);
        PostEntity savedEntity = postJpaRepository.save(entity);
        return postEntityMapper.toDomain(savedEntity);
    }
    
    @Override
    public Optional<Post> findById(PostId id) {
        return postJpaRepository.findById(id.value())
            .map(postEntityMapper::toDomain);
    }
    
    @Override
    public List<Post> findByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdAndStatus(CompanyId companyId, PostStatus status) {
        PostStatusEntity entityStatus = mapToEntityStatus(status);
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdAndStatusAndUserId(companyId.value(), entityStatus, userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdAndAuthorId(CompanyId companyId, UserId authorId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdAndAuthorIdAndUserId(companyId.value(), authorId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdAndCategoryId(CompanyId companyId, ContentCategoryId categoryId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdAndCategoryIdAndUserId(companyId.value(), categoryId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdAndPostType(CompanyId companyId, PostType postType) {
        PostTypeEntity entityType = mapToEntityPostType(postType);
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdAndPostTypeAndUserId(companyId.value(), entityType, userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findScheduledPostsUntil(LocalDateTime dateTime) {
        // Note: This method doesn't have companyId in the interface, which is a security concern
        // Using a temporary approach - should be refactored to include companyId
        Long userId = getCurrentUserId();
        Long companyId = 1L; // TODO: Get actual company ID from security context
        return postJpaRepository.findScheduledPostsUntilByCompanyIdAndUserId(dateTime, companyId, userId)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findScheduledPostsByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findScheduledPostsByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findPublishedPostsByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findPublishedPostsByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findDraftPostsByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findDraftPostsByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findFailedPostsByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findFailedPostsByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findArchivedPostsByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findArchivedPostsByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findPinnedPostsByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findPinnedPostsByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdAndCreatedBetween(CompanyId companyId, LocalDateTime start, LocalDateTime end) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdAndCreatedAtBetweenAndUserId(companyId.value(), start, end, userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdAndPublishedBetween(CompanyId companyId, LocalDateTime start, LocalDateTime end) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdAndPublishedAtBetweenAndUserId(companyId.value(), start, end, userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByTargetSocialAccount(SocialAccountId socialAccountId) {
        // Need company ID for security - temporarily using a default
        Long userId = getCurrentUserId();
        Long companyId = 1L; // TODO: Get actual company ID from security context or add to method signature
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByTargetSocialAccountAndCompanyIdAndUserId(socialAccountId.value(), companyId, userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdOrderByCreatedAtDesc(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdOrderByCreatedAtDescAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdOrderByPublishedAtDesc(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdOrderByPublishedAtDescAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findByCompanyIdOrderByScheduledForAsc(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE);
        return postJpaRepository.findByCompanyIdOrderByScheduledForAscAndUserId(companyId.value(), userId, pageable)
            .stream()
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public List<Post> findTop10MostEngagedByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        Pageable pageable = PageRequest.of(0, 10);
        return postJpaRepository.findTop10MostEngagedByCompanyIdAndUserId(companyId.value(), userId, pageable)
            .stream()
            .limit(10)
            .map(postEntityMapper::toDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public boolean existsByContentTextAndCompanyId(String contentText, CompanyId companyId) {
        return postJpaRepository.existsByContentTextAndCompanyId(contentText, companyId.value());
    }
    
    @Override
    public void delete(Post post) {
        postJpaRepository.deleteById(post.getId().value());
    }
    
    @Override
    public void deleteById(PostId id) {
        postJpaRepository.deleteById(id.value());
    }
    
    @Override
    public long countByCompanyId(CompanyId companyId) {
        Long userId = getCurrentUserId();
        return postJpaRepository.countByCompanyIdAndUserId(companyId.value(), userId);
    }
    
    @Override
    public long countByCompanyIdAndStatus(CompanyId companyId, PostStatus status) {
        Long userId = getCurrentUserId();
        PostStatusEntity entityStatus = mapToEntityStatus(status);
        return postJpaRepository.countByCompanyIdAndStatusAndUserId(companyId.value(), entityStatus, userId);
    }
    
    @Override
    public long countPublishedPostsToday(CompanyId companyId) {
        Long userId = getCurrentUserId();
        LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();
        LocalDateTime endOfDay = startOfDay.plusDays(1);
        return postJpaRepository.countPublishedPostsTodayByCompanyIdAndUserId(companyId.value(), startOfDay, endOfDay, userId);
    }
    
    @Override
    public long countScheduledPosts(CompanyId companyId) {
        Long userId = getCurrentUserId();
        return postJpaRepository.countScheduledPostsByCompanyIdAndUserId(companyId.value(), userId);
    }
    
    // Helper methods for enum conversion
    private PostStatusEntity mapToEntityStatus(PostStatus status) {
        return switch (status) {
            case DRAFT -> PostStatusEntity.DRAFT;
            case SCHEDULED -> PostStatusEntity.SCHEDULED;
            case PUBLISHING -> PostStatusEntity.PUBLISHING;
            case PUBLISHED -> PostStatusEntity.PUBLISHED;
            case ERROR -> PostStatusEntity.ERROR;
            case FAILED -> PostStatusEntity.FAILED;
            case ARCHIVED -> PostStatusEntity.ARCHIVED;
            case CANCELLED -> PostStatusEntity.CANCELLED;
        };
    }
    
    private PostTypeEntity mapToEntityPostType(PostType postType) {
        return switch (postType) {
            case FEED -> PostTypeEntity.FEED;
            case STORY -> PostTypeEntity.STORY;
            case REEL -> PostTypeEntity.REEL;
            case VIDEO -> PostTypeEntity.VIDEO;
            case CAROUSEL -> PostTypeEntity.CAROUSEL;
            case LIVE -> PostTypeEntity.LIVE;
        };
    }
}
