package com.social.media.infrastructure.persistence.analytics.mapper;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.social.media.domain.analytics.aggregate.AnalyticsReport;
import com.social.media.domain.analytics.valueobject.*;
import com.social.media.domain.company.valueobject.CompanyId;
import com.social.media.domain.shared.exception.BusinessRuleViolationException;
import com.social.media.infrastructure.persistence.analytics.entity.*;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * Mapper for AnalyticsReport domain aggregate to AnalyticsReportEntity and vice versa
 */
@Component
public class AnalyticsReportEntityMapper {
    
    private final ObjectMapper objectMapper;
    
    public AnalyticsReportEntityMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
    
    /**
     * Converts domain AnalyticsReport to AnalyticsReportEntity
     */
    public AnalyticsReportEntity toEntity(AnalyticsReport analyticsReport) {
        if (analyticsReport == null) {
            return null;
        }
        
        AnalyticsReportEntity entity = new AnalyticsReportEntity();
        entity.setId(analyticsReport.getId().getValue());
        entity.setCompanyId(analyticsReport.getCompanyId().value());
        entity.setReportType(mapReportType(analyticsReport.getReportType()));
        entity.setMetricType(mapMetricType(analyticsReport.getMetricType()));
        entity.setPeriodStart(analyticsReport.getPeriodStart());
        entity.setPeriodEnd(analyticsReport.getPeriodEnd());
        entity.setTitle(analyticsReport.getTitle());
        entity.setDescription(analyticsReport.getDescription());
        entity.setData(serializeReportData(analyticsReport.getData()));
        entity.setGeneratedAt(analyticsReport.getGeneratedAt());
        entity.setCreatedAt(analyticsReport.getCreatedAt());
        entity.setUpdatedAt(analyticsReport.getUpdatedAt());
        
        return entity;
    }
    
    /**
     * Converts AnalyticsReportEntity to domain AnalyticsReport
     */
    public AnalyticsReport toDomain(AnalyticsReportEntity entity) {
        if (entity == null) {
            return null;
        }
        
        Map<String, Object> reportData = deserializeReportData(entity.getData());
        
        return new AnalyticsReport(
            AnalyticsId.of(entity.getId()),
            CompanyId.of(entity.getCompanyId()),
            mapReportType(entity.getReportType()),
            mapMetricType(entity.getMetricType()),
            entity.getPeriodStart(),
            entity.getPeriodEnd(),
            reportData,
            entity.getTitle(),
            entity.getDescription(),
            entity.getGeneratedAt(),
            entity.getCreatedAt(),
            entity.getUpdatedAt()
        );
    }
    
    /**
     * Maps domain ReportType to entity ReportTypeEntity
     */
    private ReportTypeEntity mapReportType(ReportType reportType) {
        if (reportType == null) {
            return null;
        }
        
        return switch (reportType) {
            case ACCOUNT_OVERVIEW -> ReportTypeEntity.ACCOUNT_OVERVIEW;
            case ENGAGEMENT_ANALYSIS -> ReportTypeEntity.ENGAGEMENT_ANALYSIS;
            case CONTENT_PERFORMANCE -> ReportTypeEntity.CONTENT_PERFORMANCE;
            case GROWTH_TRACKING -> ReportTypeEntity.GROWTH_TRACKING;
            case COMPETITIVE_ANALYSIS -> ReportTypeEntity.COMPETITIVE_ANALYSIS;
            case AUDIENCE_INSIGHTS -> ReportTypeEntity.AUDIENCE_INSIGHTS;
            case HASHTAG_PERFORMANCE -> ReportTypeEntity.HASHTAG_PERFORMANCE;
            case CAMPAIGN_ANALYSIS -> ReportTypeEntity.CAMPAIGN_ANALYSIS;
            case ROI_REPORT -> ReportTypeEntity.ROI_REPORT;
            case CUSTOM_METRICS -> ReportTypeEntity.CUSTOM_METRICS;
        };
    }
    
    /**
     * Maps entity ReportTypeEntity to domain ReportType
     */
    private ReportType mapReportType(ReportTypeEntity reportTypeEntity) {
        if (reportTypeEntity == null) {
            return null;
        }
        
        return switch (reportTypeEntity) {
            case ACCOUNT_OVERVIEW -> ReportType.ACCOUNT_OVERVIEW;
            case ENGAGEMENT_ANALYSIS -> ReportType.ENGAGEMENT_ANALYSIS;
            case CONTENT_PERFORMANCE -> ReportType.CONTENT_PERFORMANCE;
            case GROWTH_TRACKING -> ReportType.GROWTH_TRACKING;
            case COMPETITIVE_ANALYSIS -> ReportType.COMPETITIVE_ANALYSIS;
            case AUDIENCE_INSIGHTS -> ReportType.AUDIENCE_INSIGHTS;
            case HASHTAG_PERFORMANCE -> ReportType.HASHTAG_PERFORMANCE;
            case CAMPAIGN_ANALYSIS -> ReportType.CAMPAIGN_ANALYSIS;
            case ROI_REPORT -> ReportType.ROI_REPORT;
            case CUSTOM_METRICS -> ReportType.CUSTOM_METRICS;
        };
    }
    
    /**
     * Maps domain MetricType to entity MetricTypeEntity
     */
    private MetricTypeEntity mapMetricType(MetricType metricType) {
        if (metricType == null) {
            return null;
        }
        
        return switch (metricType) {
            case FOLLOWERS -> MetricTypeEntity.FOLLOWERS;
            case FOLLOWING -> MetricTypeEntity.FOLLOWING;
            case POSTS -> MetricTypeEntity.POSTS;
            case LIKES -> MetricTypeEntity.LIKES;
            case COMMENTS -> MetricTypeEntity.COMMENTS;
            case SHARES -> MetricTypeEntity.SHARES;
            case VIEWS -> MetricTypeEntity.VIEWS;
            case REACH -> MetricTypeEntity.REACH;
            case ENGAGEMENT_RATE -> MetricTypeEntity.ENGAGEMENT_RATE;
            case CLICK_THROUGH_RATE -> MetricTypeEntity.CLICK_THROUGH_RATE;
            case SAVES -> MetricTypeEntity.SAVES;
            case PROFILE_VISITS -> MetricTypeEntity.PROFILE_VISITS;
            case WEBSITE_CLICKS -> MetricTypeEntity.WEBSITE_CLICKS;
            case EMAIL_CONTACTS -> MetricTypeEntity.EMAIL_CONTACTS;
            case PHONE_CONTACTS -> MetricTypeEntity.PHONE_CONTACTS;
            case MENTIONS -> MetricTypeEntity.MENTIONS;
            case HASHTAG_PERFORMANCE -> MetricTypeEntity.HASHTAG_PERFORMANCE;
            case STORY_VIEWS -> MetricTypeEntity.STORY_VIEWS;
            case REEL_PLAYS -> MetricTypeEntity.REEL_PLAYS;
            case LIVE_VIEWERS -> MetricTypeEntity.LIVE_VIEWERS;
        };
    }
    
    /**
     * Maps entity MetricTypeEntity to domain MetricType
     */
    private MetricType mapMetricType(MetricTypeEntity metricTypeEntity) {
        if (metricTypeEntity == null) {
            return null;
        }
        
        return switch (metricTypeEntity) {
            case FOLLOWERS -> MetricType.FOLLOWERS;
            case FOLLOWING -> MetricType.FOLLOWING;
            case POSTS -> MetricType.POSTS;
            case LIKES -> MetricType.LIKES;
            case COMMENTS -> MetricType.COMMENTS;
            case SHARES -> MetricType.SHARES;
            case VIEWS -> MetricType.VIEWS;
            case REACH -> MetricType.REACH;
            case ENGAGEMENT_RATE -> MetricType.ENGAGEMENT_RATE;
            case CLICK_THROUGH_RATE -> MetricType.CLICK_THROUGH_RATE;
            case SAVES -> MetricType.SAVES;
            case PROFILE_VISITS -> MetricType.PROFILE_VISITS;
            case WEBSITE_CLICKS -> MetricType.WEBSITE_CLICKS;
            case EMAIL_CONTACTS -> MetricType.EMAIL_CONTACTS;
            case PHONE_CONTACTS -> MetricType.PHONE_CONTACTS;
            case MENTIONS -> MetricType.MENTIONS;
            case HASHTAG_PERFORMANCE -> MetricType.HASHTAG_PERFORMANCE;
            case STORY_VIEWS -> MetricType.STORY_VIEWS;
            case REEL_PLAYS -> MetricType.REEL_PLAYS;
            case LIVE_VIEWERS -> MetricType.LIVE_VIEWERS;
        };
    }
    
    /**
     * Serializes report data map to JSON string map for database storage
     */
    private Map<String, String> serializeReportData(Map<String, Object> reportData) {
        if (reportData == null || reportData.isEmpty()) {
            return Map.of();
        }
        
        try {
            // Convert each object value to JSON string
            return reportData.entrySet().stream()
                .collect(java.util.stream.Collectors.toMap(
                    Map.Entry::getKey,
                    entry -> {
                        try {
                            return objectMapper.writeValueAsString(entry.getValue());
                        } catch (JsonProcessingException e) {
                            throw new BusinessRuleViolationException("Failed to serialize report data entry: " + entry.getKey());
                        }
                    }
                ));
        } catch (Exception e) {
            throw new BusinessRuleViolationException("Failed to serialize report data");
        }
    }
    
    /**
     * Deserializes JSON string map to report data map
     */
    private Map<String, Object> deserializeReportData(Map<String, String> reportDataJson) {
        if (reportDataJson == null || reportDataJson.isEmpty()) {
            return Map.of();
        }
        
        try {
            // Convert each JSON string value back to object
            return reportDataJson.entrySet().stream()
                .collect(java.util.stream.Collectors.toMap(
                    Map.Entry::getKey,
                    entry -> {
                        try {
                            return objectMapper.readValue(entry.getValue(), Object.class);
                        } catch (JsonProcessingException e) {
                            throw new BusinessRuleViolationException("Failed to deserialize report data entry: " + entry.getKey());
                        }
                    }
                ));
        } catch (Exception e) {
            throw new BusinessRuleViolationException("Failed to deserialize report data");
        }
    }
}
