package com.social.media.mapper;

import com.social.media.dto.PostMediaDto;
import com.social.media.domain.entity.PostMedia;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Mapper utility for PostMedia entity and DTO conversions
 */
@Component
public class PostMediaMapper {
    
    /**
     * Convert PostMedia entity to DTO
     * @param entity the PostMedia entity
     * @return the PostMediaDto
     */
    public PostMediaDto toDto(PostMedia entity) {
        if (entity == null) {
            return null;
        }
        
        PostMediaDto dto = new PostMediaDto();
        dto.setId(entity.getId());
        dto.setPostId(entity.getPost() != null ? entity.getPost().getId() : entity.getPostId());
        dto.setMediaId(entity.getMedia() != null ? entity.getMedia().getId() : entity.getMediaId());
        dto.setDisplayOrder(entity.getDisplayOrder());
        dto.setIsPrimary(entity.getIsPrimary());
        dto.setCreatedAt(entity.getCreatedAt());
        
        return dto;
    }
    
    /**
     * Convert PostMediaDto to entity
     * @param dto the PostMediaDto
     * @return the PostMedia entity
     */
    public PostMedia toEntity(PostMediaDto dto) {
        if (dto == null) {
            return null;
        }
        
        PostMedia entity = new PostMedia();
        
        // Set ID if present (for updates)
        if (dto.getId() != null) {
            entity.setId(dto.getId());
        }
        
        // Set foreign keys
        if (dto.getPostId() != null) {
            entity.setPostId(dto.getPostId());
        }
        if (dto.getMediaId() != null) {
            entity.setMediaId(dto.getMediaId());
        }
        
        // Set display order and primary status
        entity.setDisplayOrder(dto.getDisplayOrder());
        entity.setIsPrimary(dto.getIsPrimary());
        
        // Set timestamps if present (usually for updates)
        if (dto.getCreatedAt() != null) {
            entity.setCreatedAt(dto.getCreatedAt());
        }
        
        // Note: Post and Media entities are typically set by the service layer
        // to avoid loading entities unnecessarily
        
        return entity;
    }
    
    /**
     * Convert a list of PostMedia entities to DTOs
     * @param entities list of PostMedia entities
     * @return list of PostMediaDto
     */
    public List<PostMediaDto> toDtoList(List<PostMedia> entities) {
        if (entities == null || entities.isEmpty()) {
            return Collections.emptyList();
        }
        
        return entities.stream()
                .map(this::toDto)
                .collect(Collectors.toList());
    }
    
    /**
     * Convert a list of PostMediaDto to entities
     * @param dtos list of PostMediaDto
     * @return list of PostMedia entities
     */
    public List<PostMedia> toEntityList(List<PostMediaDto> dtos) {
        if (dtos == null || dtos.isEmpty()) {
            return Collections.emptyList();
        }
        
        return dtos.stream()
                .map(this::toEntity)
                .collect(Collectors.toList());
    }
    
    /**
     * Update existing entity with DTO data
     * @param entity existing PostMedia entity
     * @param dto PostMediaDto with updated data
     * @return updated entity
     */
    public PostMedia updateEntity(PostMedia entity, PostMediaDto dto) {
        if (entity == null || dto == null) {
            return entity;
        }
        
        // Update mutable fields
        if (dto.getDisplayOrder() != null) {
            entity.setDisplayOrder(dto.getDisplayOrder());
        }
        
        if (dto.getIsPrimary() != null) {
            entity.setIsPrimary(dto.getIsPrimary());
        }
        
        // Note: Post and Media relationships are typically not updated
        // through this method to maintain referential integrity
        
        return entity;
    }
    
    /**
     * Create a new entity from DTO for create operations
     * @param dto PostMediaDto
     * @param postId ID of the post
     * @param mediaId ID of the media
     * @return new PostMedia entity
     */
    public PostMedia createEntity(PostMediaDto dto, Long postId, Long mediaId) {
        if (dto == null) {
            return null;
        }
        
        PostMedia entity = new PostMedia();
        
        // Set display order and primary status from DTO
        entity.setDisplayOrder(dto.getDisplayOrder());
        entity.setIsPrimary(dto.getIsPrimary() != null ? dto.getIsPrimary() : false);
        
        // Note: Post and Media entities will be set by the service layer
        // using the provided IDs
        
        return entity;
    }
    
    /**
     * Create a minimal DTO with just the essential fields
     * @param entity PostMedia entity
     * @return minimal PostMediaDto
     */
    public PostMediaDto toMinimalDto(PostMedia entity) {
        if (entity == null) {
            return null;
        }
        
        PostMediaDto dto = new PostMediaDto();
        dto.setId(entity.getId());
        dto.setPostId(entity.getPost() != null ? entity.getPost().getId() : entity.getPostId());
        dto.setMediaId(entity.getMedia() != null ? entity.getMedia().getId() : entity.getMediaId());
        dto.setDisplayOrder(entity.getDisplayOrder());
        dto.setIsPrimary(entity.getIsPrimary());
        
        return dto;
    }
    
    /**
     * Create a detailed DTO with all fields including computed ones
     * @param entity PostMedia entity
     * @return detailed PostMediaDto
     */
    public PostMediaDto toDetailedDto(PostMedia entity) {
        if (entity == null) {
            return null;
        }
        
        PostMediaDto dto = toDto(entity);
        
        // The computed fields will be automatically calculated by the DTO
        // when accessing the getters due to the implementation in PostMediaDto
        
        return dto;
    }
    
    /**
     * Convert entity to DTO with position information
     * @param entity PostMedia entity
     * @param totalCount total number of media for the post
     * @return PostMediaDto with position context
     */
    public PostMediaDto toDtoWithPosition(PostMedia entity, long totalCount) {
        if (entity == null) {
            return null;
        }
        
        PostMediaDto dto = toDto(entity);
        
        // Position information is computed automatically by the DTO
        // but we can provide total count context if needed
        
        return dto;
    }
    
    /**
     * Convert entity to DTO for API responses
     * @param entity PostMedia entity
     * @return PostMediaDto optimized for API responses
     */
    public PostMediaDto toApiDto(PostMedia entity) {
        if (entity == null) {
            return null;
        }
        
        // For API responses, we typically want all fields
        return toDetailedDto(entity);
    }
    
    /**
     * Convert a list of entities to API DTOs
     * @param entities list of PostMedia entities
     * @return list of PostMediaDto for API responses
     */
    public List<PostMediaDto> toApiDtoList(List<PostMedia> entities) {
        if (entities == null || entities.isEmpty()) {
            return Collections.emptyList();
        }
        
        return entities.stream()
                .map(this::toApiDto)
                .collect(Collectors.toList());
    }
    
    /**
     * Create a DTO for creating new post-media relationships
     * @param postId ID of the post
     * @param mediaId ID of the media
     * @param displayOrder display order
     * @param isPrimary whether this is primary media
     * @return PostMediaDto for creation
     */
    public PostMediaDto createDto(Long postId, Long mediaId, Integer displayOrder, Boolean isPrimary) {
        PostMediaDto dto = new PostMediaDto();
        dto.setPostId(postId);
        dto.setMediaId(mediaId);
        dto.setDisplayOrder(displayOrder);
        dto.setIsPrimary(isPrimary != null ? isPrimary : false);
        
        return dto;
    }
    
    /**
     * Create DTOs for batch operations
     * @param postId ID of the post
     * @param mediaIds list of media IDs
     * @param startingOrder starting display order
     * @param firstIsPrimary whether the first media should be primary
     * @return list of PostMediaDto for batch creation
     */
    public List<PostMediaDto> createBatchDtos(Long postId, List<Long> mediaIds, 
                                             Integer startingOrder, Boolean firstIsPrimary) {
        if (mediaIds == null || mediaIds.isEmpty()) {
            return Collections.emptyList();
        }
        
        List<PostMediaDto> dtos = new java.util.ArrayList<>();
        int order = startingOrder != null ? startingOrder : 1;
        
        for (int i = 0; i < mediaIds.size(); i++) {
            Long mediaId = mediaIds.get(i);
            boolean isPrimary = (firstIsPrimary != null && firstIsPrimary && i == 0);
            
            PostMediaDto dto = createDto(postId, mediaId, order + i, isPrimary);
            dtos.add(dto);
        }
        
        return dtos;
    }
    
    /**
     * Copy DTO with new IDs (for copying operations)
     * @param source source PostMediaDto
     * @param newPostId new post ID
     * @param newMediaId new media ID (optional, can be null to keep same media)
     * @return copied PostMediaDto
     */
    public PostMediaDto copyDto(PostMediaDto source, Long newPostId, Long newMediaId) {
        if (source == null) {
            return null;
        }
        
        PostMediaDto dto = new PostMediaDto();
        dto.setPostId(newPostId);
        dto.setMediaId(newMediaId != null ? newMediaId : source.getMediaId());
        dto.setDisplayOrder(source.getDisplayOrder());
        dto.setIsPrimary(source.getIsPrimary());
        
        return dto;
    }
    
    /**
     * Merge two DTOs (useful for updates)
     * @param target target DTO to update
     * @param source source DTO with new values
     * @return merged DTO
     */
    public PostMediaDto mergeDto(PostMediaDto target, PostMediaDto source) {
        if (target == null) {
            return source;
        }
        if (source == null) {
            return target;
        }
        
        PostMediaDto dto = new PostMediaDto();
        dto.setId(target.getId()); // Keep original ID
        dto.setPostId(source.getPostId() != null ? source.getPostId() : target.getPostId());
        dto.setMediaId(source.getMediaId() != null ? source.getMediaId() : target.getMediaId());
        dto.setDisplayOrder(source.getDisplayOrder() != null ? source.getDisplayOrder() : target.getDisplayOrder());
        dto.setIsPrimary(source.getIsPrimary() != null ? source.getIsPrimary() : target.getIsPrimary());
        dto.setCreatedAt(target.getCreatedAt()); // Keep original timestamps
        
        return dto;
    }
}
