package com.social.media.service.impl;

import com.social.media.dto.PostDto;
import com.social.media.service.PostService;
import com.social.media.domain.shared.PageResponse;
import com.social.media.domain.entity.Post;
import com.social.media.repository.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional
public class PostServiceImpl implements PostService {
    
    @Autowired
    private PostRepository postRepository;
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getAllPosts(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.findAll(pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PostDto getPostById(Long id) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Post not found with id: " + id));
        return convertToDto(post);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PostDto getPostByPostCode(String postCode) {
        Post post = postRepository.findByPostCode(postCode)
                .orElseThrow(() -> new RuntimeException("Post not found with code: " + postCode));
        return convertToDto(post);
    }
    
    @Override
    public PostDto createPost(PostDto postDto) {
        Post post = convertToEntity(postDto);
        
        // Generate post code if not provided
        if (post.getPostCode() == null) {
            // The post code will be auto-generated by the database sequence
        }
        
        Post savedPost = postRepository.save(post);
        return convertToDto(savedPost);
    }
    
    @Override
    public PostDto updatePost(Long id, PostDto postDto) {
        Post existingPost = postRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Post not found with id: " + id));
        
        updateEntityFromDto(existingPost, postDto);
        Post savedPost = postRepository.save(existingPost);
        return convertToDto(savedPost);
    }
    
    @Override
    public void deletePost(Long id) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Post not found with id: " + id));
        post.archive(); // Soft delete
        postRepository.save(post);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPostsByCompany(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.findByCompanyIdAndDeletedAtIsNull(companyId, pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPostsByAuthor(Long authorId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.findByAuthorIdAndDeletedAtIsNull(authorId, pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPostsByStatus(Post.PostStatus status, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.findByStatusAndDeletedAtIsNull(status, pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPostsByType(Post.PostType postType, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.findByPostTypeAndDeletedAtIsNull(postType, pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPostsByCompanyAndStatus(Long companyId, Post.PostStatus status, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.findByCompanyIdAndStatusAndDeletedAtIsNull(companyId, status, pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPinnedPosts(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.findByCompanyIdAndIsPinnedTrueAndDeletedAtIsNull(companyId, pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    public PostDto publishPost(Long id) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Post not found with id: " + id));
        
        post.publish();
        Post savedPost = postRepository.save(post);
        return convertToDto(savedPost);
    }
    
    @Override
    public PostDto schedulePost(Long id, LocalDateTime scheduledTime) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Post not found with id: " + id));
        
        post.schedule(scheduledTime);
        Post savedPost = postRepository.save(post);
        return convertToDto(savedPost);
    }
    
    @Override
    public PostDto pinPost(Long id) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Post not found with id: " + id));
        
        post.pin();
        Post savedPost = postRepository.save(post);
        return convertToDto(savedPost);
    }
    
    @Override
    public PostDto unpinPost(Long id) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Post not found with id: " + id));
        
        post.unpin();
        Post savedPost = postRepository.save(post);
        return convertToDto(savedPost);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<PostDto> getScheduledPostsForExecution() {
        List<Post> posts = postRepository.findScheduledPostsForExecution(LocalDateTime.now());
        return posts.stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> searchPosts(Long companyId, String searchTerm, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.searchPostsByContent(companyId, searchTerm, pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPopularPosts(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        Page<Post> postPage = postRepository.findPopularPostsByCompany(companyId, pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, postPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long getTotalEngagement(Long companyId) {
        Long engagement = postRepository.getTotalEngagementByCompany(companyId);
        return engagement != null ? engagement : 0L;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long getPostCount(Long companyId) {
        return postRepository.countPostsByCompany(companyId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long getPostCountByStatus(Long companyId, Post.PostStatus status) {
        return postRepository.countPostsByCompanyAndStatus(companyId, status);
    }
    
    // Legacy methods for backward compatibility
    @Override
    @Transactional(readOnly = true)
    public PostDto getPostById(String id) {
        try {
            return getPostById(Long.parseLong(id));
        } catch (NumberFormatException e) {
            return getPostByPostCode(id);
        }
    }
    
    @Override
    public PostDto updatePost(String id, PostDto postDto) {
        try {
            return updatePost(Long.parseLong(id), postDto);
        } catch (NumberFormatException e) {
            Post post = postRepository.findByPostCode(id)
                    .orElseThrow(() -> new RuntimeException("Post not found with code: " + id));
            updateEntityFromDto(post, postDto);
            Post savedPost = postRepository.save(post);
            return convertToDto(savedPost);
        }
    }
    
    @Override
    public void deletePost(String id) {
        try {
            deletePost(Long.parseLong(id));
        } catch (NumberFormatException e) {
            Post post = postRepository.findByPostCode(id)
                    .orElseThrow(() -> new RuntimeException("Post not found with code: " + id));
            post.archive();
            postRepository.save(post);
        }
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPostsByPlatform(String platform, int page, int size) {
        // For backward compatibility, we can map platform to post type or use a different approach
        // This is a simplified implementation
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<Post> postPage = postRepository.findAll(pageable);
        
        List<PostDto> postDtos = postPage.getContent().stream()
                .map(this::convertToDto)
                .filter(dto -> platform.equals(dto.getPlatform()))
                .collect(Collectors.toList());
        
        return PageResponse.of(postDtos, (long) postDtos.size(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<PostDto> getPostsByStatus(String status, int page, int size) {
        try {
            Post.PostStatus postStatus = Post.PostStatus.valueOf(status.toUpperCase());
            return getPostsByStatus(postStatus, page, size);
        } catch (IllegalArgumentException e) {
            // Return empty page if status is invalid
            return PageResponse.of(List.of(), 0L, page, size);
        }
    }
    
    // Utility methods for conversion
    private PostDto convertToDto(Post post) {
        PostDto dto = new PostDto();
        dto.setId(post.getId());
        dto.setPostCode(post.getPostCode());
        dto.setCompanyId(post.getCompanyId());
        dto.setAuthorId(post.getAuthorId());
        dto.setCategoryId(post.getCategoryId());
        dto.setContentText(post.getContentText());
        dto.setPostType(post.getPostType() != null ? post.getPostType().name() : null);
        dto.setStatus(post.getStatus() != null ? post.getStatus().name() : null);
        dto.setScheduledFor(post.getScheduledFor());
        dto.setPublishedAt(post.getPublishedAt());
        dto.setCreatedAt(post.getCreatedAt());
        dto.setUpdatedAt(post.getUpdatedAt());
        dto.setTags(post.getTags());
        dto.setIsPinned(post.getIsPinned());
        dto.setViewCount(post.getViewCount());
        dto.setLikeCount(post.getLikeCount());
        dto.setShareCount(post.getShareCount());
        dto.setCommentCount(post.getCommentCount());
        dto.setDeletedAt(post.getDeletedAt());
        
        // Set metrics for legacy compatibility
        if (post.getLikeCount() != null || post.getShareCount() != null || 
            post.getCommentCount() != null || post.getViewCount() != null) {
            PostDto.PostMetricsDto metrics = new PostDto.PostMetricsDto();
            metrics.setLikes(post.getLikeCount());
            metrics.setShares(post.getShareCount());
            metrics.setComments(post.getCommentCount());
            metrics.setViews(post.getViewCount());
            
            // Calculate simple engagement rate
            if (post.getViewCount() != null && post.getViewCount() > 0) {
                int totalEngagement = (post.getLikeCount() != null ? post.getLikeCount() : 0) +
                                    (post.getShareCount() != null ? post.getShareCount() : 0) +
                                    (post.getCommentCount() != null ? post.getCommentCount() : 0);
                double engagementRate = (double) totalEngagement / post.getViewCount() * 100;
                metrics.setEngagement(engagementRate);
            }
            
            dto.setMetrics(metrics);
        }
        
        return dto;
    }
    
    private Post convertToEntity(PostDto dto) {
        Post post = new Post();
        
        if (dto.getId() != null) {
            post.setId(dto.getId());
        }
        if (dto.getPostCode() != null) {
            post.setPostCode(dto.getPostCode());
        }
        
        post.setCompanyId(dto.getCompanyId());
        post.setAuthorId(dto.getAuthorId());
        post.setCategoryId(dto.getCategoryId());
        post.setContentText(dto.getContentText());
        
        if (dto.getPostType() != null) {
            try {
                post.setPostType(Post.PostType.valueOf(dto.getPostType().toUpperCase()));
            } catch (IllegalArgumentException e) {
                post.setPostType(Post.PostType.FEED); // Default
            }
        }
        
        if (dto.getStatus() != null) {
            try {
                post.setStatus(Post.PostStatus.valueOf(dto.getStatus().toUpperCase()));
            } catch (IllegalArgumentException e) {
                post.setStatus(Post.PostStatus.DRAFT); // Default
            }
        }
        
        post.setScheduledFor(dto.getScheduledFor());
        post.setPublishedAt(dto.getPublishedAt());
        post.setTags(dto.getTags());
        post.setIsPinned(dto.getIsPinned());
        post.setViewCount(dto.getViewCount());
        post.setLikeCount(dto.getLikeCount());
        post.setShareCount(dto.getShareCount());
        post.setCommentCount(dto.getCommentCount());
        post.setDeletedAt(dto.getDeletedAt());
        
        return post;
    }
    
    private void updateEntityFromDto(Post post, PostDto dto) {
        if (dto.getContentText() != null) {
            post.setContentText(dto.getContentText());
        }
        if (dto.getPostType() != null) {
            try {
                post.setPostType(Post.PostType.valueOf(dto.getPostType().toUpperCase()));
            } catch (IllegalArgumentException e) {
                // Keep existing value
            }
        }
        if (dto.getStatus() != null) {
            try {
                post.setStatus(Post.PostStatus.valueOf(dto.getStatus().toUpperCase()));
            } catch (IllegalArgumentException e) {
                // Keep existing value
            }
        }
        if (dto.getScheduledFor() != null) {
            post.setScheduledFor(dto.getScheduledFor());
        }
        if (dto.getTags() != null) {
            post.setTags(dto.getTags());
        }
        if (dto.getIsPinned() != null) {
            post.setIsPinned(dto.getIsPinned());
        }
        if (dto.getCategoryId() != null) {
            post.setCategoryId(dto.getCategoryId());
        }
    }
}
