package com.social.media.domain.analytics;

import com.social.media.domain.shared.BaseEntity;
import com.social.media.domain.shared.enums.SocialPlatform;

import java.time.LocalDateTime;
import java.util.UUID;
import java.util.Objects;
import java.util.Map;
import java.util.HashMap;

/**
 * Analytics Report aggregate root representing comprehensive analytics data.
 * Provides insights into social media performance across platforms.
 */
public class AnalyticsReport extends BaseEntity {
    
    public enum ReportType {
        DAILY("Daily Report", "Daily analytics summary"),
        WEEKLY("Weekly Report", "Weekly analytics summary"),
        MONTHLY("Monthly Report", "Monthly analytics summary"),
        QUARTERLY("Quarterly Report", "Quarterly analytics summary"),
        YEARLY("Yearly Report", "Yearly analytics summary"),
        CUSTOM("Custom Report", "Custom date range report"),
        CAMPAIGN("Campaign Report", "Campaign-specific analytics"),
        PLATFORM("Platform Report", "Platform-specific analytics"),
        COMPETITOR("Competitor Report", "Competitor analysis report");
        
        private final String displayName;
        private final String description;
        
        ReportType(String displayName, String description) {
            this.displayName = displayName;
            this.description = description;
        }
        
        public String getDisplayName() { return displayName; }
        public String getDescription() { return description; }
    }
    
    public enum ReportStatus {
        GENERATING("Generating", "Report is being generated"),
        COMPLETED("Completed", "Report generation completed"),
        FAILED("Failed", "Report generation failed"),
        CANCELLED("Cancelled", "Report generation was cancelled"),
        SCHEDULED("Scheduled", "Report is scheduled for generation");
        
        private final String displayName;
        private final String description;
        
        ReportStatus(String displayName, String description) {
            this.displayName = displayName;
            this.description = description;
        }
        
        public String getDisplayName() { return displayName; }
        public String getDescription() { return description; }
    }
    
    private UUID companyId;
    private UUID userId;
    private String reportName;
    private ReportType reportType;
    private ReportStatus status;
    private LocalDateTime startDate;
    private LocalDateTime endDate;
    private SocialPlatform platform; // null for all platforms
    private String campaignId; // for campaign reports
    private String competitorId; // for competitor reports
    
    // Summary metrics
    private Long totalFollowers;
    private Long totalFollowing;
    private Long totalPosts;
    private Long totalLikes;
    private Long totalComments;
    private Long totalShares;
    private Long totalViews;
    private Long totalImpressions;
    private Long totalReach;
    private Double averageEngagementRate;
    private Double followerGrowthRate;
    private Integer postsPublished;
    private Integer storiesPublished;
    private Integer reelsPublished;
    
    // Platform-specific data
    private Map<String, Object> platformMetrics;
    private Map<String, Object> demographicData;
    private Map<String, Object> contentPerformance;
    private Map<String, Object> engagementTrends;
    private Map<String, Object> audienceInsights;
    
    // Report metadata
    private LocalDateTime generatedAt;
    private LocalDateTime scheduledAt;
    private String generatedBy;
    private String reportUrl;
    private String reportFormat; // JSON, PDF, CSV, XLSX
    private Long reportSize;
    private String errorMessage;
    private Integer retryCount;
    private LocalDateTime lastRetryAt;
    private boolean isRecurring;
    private String recurringSchedule; // CRON expression
    private LocalDateTime nextGenerationAt;
    
    // Default constructor for JPA
    protected AnalyticsReport() {
        super();
    }
    
    public AnalyticsReport(
            UUID companyId,
            UUID userId,
            String reportName,
            ReportType reportType,
            LocalDateTime startDate,
            LocalDateTime endDate) {
        
        super();
        this.companyId = validateCompanyId(companyId);
        this.userId = validateUserId(userId);
        this.reportName = validateReportName(reportName);
        this.reportType = validateReportType(reportType);
        this.startDate = validateStartDate(startDate);
        this.endDate = validateEndDate(endDate, startDate);
        this.status = ReportStatus.SCHEDULED;
        this.platformMetrics = new HashMap<>();
        this.demographicData = new HashMap<>();
        this.contentPerformance = new HashMap<>();
        this.engagementTrends = new HashMap<>();
        this.audienceInsights = new HashMap<>();
        this.isRecurring = false;
        this.retryCount = 0;
        this.reportFormat = "JSON";
    }
    
    // Validation methods
    private UUID validateCompanyId(UUID companyId) {
        if (companyId == null) {
            throw new IllegalArgumentException("Company ID cannot be null");
        }
        return companyId;
    }
    
    private UUID validateUserId(UUID userId) {
        if (userId == null) {
            throw new IllegalArgumentException("User ID cannot be null");
        }
        return userId;
    }
    
    private String validateReportName(String reportName) {
        if (reportName == null || reportName.trim().isEmpty()) {
            throw new IllegalArgumentException("Report name cannot be null or empty");
        }
        if (reportName.length() > 200) {
            throw new IllegalArgumentException("Report name cannot exceed 200 characters");
        }
        return reportName.trim();
    }
    
    private ReportType validateReportType(ReportType reportType) {
        if (reportType == null) {
            throw new IllegalArgumentException("Report type cannot be null");
        }
        return reportType;
    }
    
    private LocalDateTime validateStartDate(LocalDateTime startDate) {
        if (startDate == null) {
            throw new IllegalArgumentException("Start date cannot be null");
        }
        if (startDate.isAfter(LocalDateTime.now())) {
            throw new IllegalArgumentException("Start date cannot be in the future");
        }
        return startDate;
    }
    
    private LocalDateTime validateEndDate(LocalDateTime endDate, LocalDateTime startDate) {
        if (endDate == null) {
            throw new IllegalArgumentException("End date cannot be null");
        }
        if (endDate.isBefore(startDate)) {
            throw new IllegalArgumentException("End date cannot be before start date");
        }
        if (endDate.isAfter(LocalDateTime.now())) {
            throw new IllegalArgumentException("End date cannot be in the future");
        }
        return endDate;
    }
    
    // Factory methods
    
    /**
     * Creates a daily report for yesterday.
     */
    public static AnalyticsReport createDailyReport(UUID companyId, UUID userId) {
        LocalDateTime yesterday = LocalDateTime.now().minusDays(1).withHour(0).withMinute(0).withSecond(0);
        LocalDateTime endOfYesterday = yesterday.withHour(23).withMinute(59).withSecond(59);
        
        return new AnalyticsReport(
            companyId,
            userId,
            "Daily Report - " + yesterday.toLocalDate(),
            ReportType.DAILY,
            yesterday,
            endOfYesterday
        );
    }
    
    /**
     * Creates a weekly report for last week.
     */
    public static AnalyticsReport createWeeklyReport(UUID companyId, UUID userId) {
        LocalDateTime lastWeekStart = LocalDateTime.now().minusWeeks(1).withHour(0).withMinute(0).withSecond(0);
        LocalDateTime lastWeekEnd = lastWeekStart.plusDays(6).withHour(23).withMinute(59).withSecond(59);
        
        return new AnalyticsReport(
            companyId,
            userId,
            "Weekly Report - Week of " + lastWeekStart.toLocalDate(),
            ReportType.WEEKLY,
            lastWeekStart,
            lastWeekEnd
        );
    }
    
    /**
     * Creates a monthly report for last month.
     */
    public static AnalyticsReport createMonthlyReport(UUID companyId, UUID userId) {
        LocalDateTime lastMonthStart = LocalDateTime.now().minusMonths(1).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
        LocalDateTime lastMonthEnd = lastMonthStart.plusMonths(1).minusDays(1).withHour(23).withMinute(59).withSecond(59);
        
        return new AnalyticsReport(
            companyId,
            userId,
            "Monthly Report - " + lastMonthStart.getMonth() + " " + lastMonthStart.getYear(),
            ReportType.MONTHLY,
            lastMonthStart,
            lastMonthEnd
        );
    }
    
    /**
     * Creates a custom report for specific date range.
     */
    public static AnalyticsReport createCustomReport(
            UUID companyId,
            UUID userId,
            String reportName,
            LocalDateTime startDate,
            LocalDateTime endDate) {
        
        return new AnalyticsReport(companyId, userId, reportName, ReportType.CUSTOM, startDate, endDate);
    }
    
    // Business methods
    
    /**
     * Starts the report generation process.
     */
    public void startGeneration() {
        if (status != ReportStatus.SCHEDULED) {
            throw new IllegalStateException("Only scheduled reports can be started");
        }
        
        this.status = ReportStatus.GENERATING;
        this.generatedAt = LocalDateTime.now();
        this.markAsModified();
    }
    
    /**
     * Marks the report as completed with metrics.
     */
    public void completeGeneration(
            Long totalFollowers,
            Long totalPosts,
            Long totalLikes,
            Long totalComments,
            Double averageEngagementRate) {
        
        if (status != ReportStatus.GENERATING) {
            throw new IllegalStateException("Only generating reports can be completed");
        }
        
        this.status = ReportStatus.COMPLETED;
        this.totalFollowers = totalFollowers;
        this.totalPosts = totalPosts;
        this.totalLikes = totalLikes;
        this.totalComments = totalComments;
        this.averageEngagementRate = averageEngagementRate;
        this.errorMessage = null;
        this.markAsModified();
    }
    
    /**
     * Marks the report generation as failed.
     */
    public void markAsFailed(String errorMessage) {
        this.status = ReportStatus.FAILED;
        this.errorMessage = errorMessage;
        this.retryCount = (this.retryCount != null ? this.retryCount : 0) + 1;
        this.lastRetryAt = LocalDateTime.now();
        this.markAsModified();
    }
    
    /**
     * Cancels the report generation.
     */
    public void cancel() {
        if (status == ReportStatus.COMPLETED) {
            throw new IllegalStateException("Cannot cancel completed reports");
        }
        
        this.status = ReportStatus.CANCELLED;
        this.markAsModified();
    }
    
    /**
     * Schedules the report for later generation.
     */
    public void schedule(LocalDateTime scheduledAt) {
        if (scheduledAt == null || scheduledAt.isBefore(LocalDateTime.now())) {
            throw new IllegalArgumentException("Scheduled time must be in the future");
        }
        
        this.status = ReportStatus.SCHEDULED;
        this.scheduledAt = scheduledAt;
        this.markAsModified();
    }
    
    /**
     * Updates platform-specific metrics.
     */
    public void updatePlatformMetrics(Map<String, Object> metrics) {
        if (metrics != null) {
            this.platformMetrics.clear();
            this.platformMetrics.putAll(metrics);
            this.markAsModified();
        }
    }
    
    /**
     * Updates demographic data.
     */
    public void updateDemographicData(Map<String, Object> demographics) {
        if (demographics != null) {
            this.demographicData.clear();
            this.demographicData.putAll(demographics);
            this.markAsModified();
        }
    }
    
    /**
     * Updates content performance data.
     */
    public void updateContentPerformance(Map<String, Object> performance) {
        if (performance != null) {
            this.contentPerformance.clear();
            this.contentPerformance.putAll(performance);
            this.markAsModified();
        }
    }
    
    /**
     * Sets up recurring report generation.
     */
    public void setupRecurring(String cronSchedule) {
        if (cronSchedule == null || cronSchedule.trim().isEmpty()) {
            throw new IllegalArgumentException("CRON schedule cannot be null or empty");
        }
        
        this.isRecurring = true;
        this.recurringSchedule = cronSchedule.trim();
        // Calculate next generation time based on CRON expression
        this.nextGenerationAt = calculateNextGeneration(cronSchedule);
        this.markAsModified();
    }
    
    /**
     * Removes recurring schedule.
     */
    public void removeRecurring() {
        this.isRecurring = false;
        this.recurringSchedule = null;
        this.nextGenerationAt = null;
        this.markAsModified();
    }
    
    /**
     * Updates the report URL after generation.
     */
    public void updateReportUrl(String reportUrl, String format, Long size) {
        this.reportUrl = reportUrl;
        this.reportFormat = format;
        this.reportSize = size;
        this.markAsModified();
    }
    
    /**
     * Sets the platform filter for the report.
     */
    public void setPlatformFilter(SocialPlatform platform) {
        this.platform = platform;
        this.markAsModified();
    }
    
    /**
     * Sets campaign filter for campaign reports.
     */
    public void setCampaignFilter(String campaignId) {
        this.campaignId = campaignId;
        this.markAsModified();
    }
    
    // Helper methods
    
    private LocalDateTime calculateNextGeneration(String cronSchedule) {
        // This would typically use a CRON parser library
        // For now, return a simple daily schedule
        return LocalDateTime.now().plusDays(1);
    }
    
    // Query methods
    
    /**
     * Checks if the report is ready for generation.
     */
    public boolean isReadyForGeneration() {
        return status == ReportStatus.SCHEDULED &&
               (scheduledAt == null || scheduledAt.isBefore(LocalDateTime.now()));
    }
    
    /**
     * Checks if the report generation is complete.
     */
    public boolean isCompleted() {
        return status == ReportStatus.COMPLETED;
    }
    
    /**
     * Checks if the report generation failed.
     */
    public boolean isFailed() {
        return status == ReportStatus.FAILED;
    }
    
    /**
     * Checks if the report should be retried.
     */
    public boolean shouldRetry() {
        return status == ReportStatus.FAILED && 
               retryCount != null && 
               retryCount < 3;
    }
    
    /**
     * Gets the report duration in days.
     */
    public long getDurationInDays() {
        return java.time.Duration.between(startDate, endDate).toDays() + 1;
    }
    
    /**
     * Checks if this is a recent report.
     */
    public boolean isRecent(int days) {
        return generatedAt != null && 
               generatedAt.isAfter(LocalDateTime.now().minusDays(days));
    }
    
    /**
     * Gets engagement rate category.
     */
    public String getEngagementCategory() {
        if (averageEngagementRate == null) {
            return "UNKNOWN";
        }
        if (averageEngagementRate >= 6.0) {
            return "EXCELLENT";
        } else if (averageEngagementRate >= 3.0) {
            return "GOOD";
        } else if (averageEngagementRate >= 1.0) {
            return "AVERAGE";
        } else {
            return "POOR";
        }
    }
    
    // Getters
    public UUID getCompanyId() { return companyId; }
    public UUID getUserId() { return userId; }
    public String getReportName() { return reportName; }
    public ReportType getReportType() { return reportType; }
    public ReportStatus getStatus() { return status; }
    public LocalDateTime getStartDate() { return startDate; }
    public LocalDateTime getEndDate() { return endDate; }
    public SocialPlatform getPlatform() { return platform; }
    public String getCampaignId() { return campaignId; }
    public String getCompetitorId() { return competitorId; }
    public Long getTotalFollowers() { return totalFollowers; }
    public Long getTotalFollowing() { return totalFollowing; }
    public Long getTotalPosts() { return totalPosts; }
    public Long getTotalLikes() { return totalLikes; }
    public Long getTotalComments() { return totalComments; }
    public Long getTotalShares() { return totalShares; }
    public Long getTotalViews() { return totalViews; }
    public Long getTotalImpressions() { return totalImpressions; }
    public Long getTotalReach() { return totalReach; }
    public Double getAverageEngagementRate() { return averageEngagementRate; }
    public Double getFollowerGrowthRate() { return followerGrowthRate; }
    public Integer getPostsPublished() { return postsPublished; }
    public Integer getStoriesPublished() { return storiesPublished; }
    public Integer getReelsPublished() { return reelsPublished; }
    public Map<String, Object> getPlatformMetrics() { return new HashMap<>(platformMetrics); }
    public Map<String, Object> getDemographicData() { return new HashMap<>(demographicData); }
    public Map<String, Object> getContentPerformance() { return new HashMap<>(contentPerformance); }
    public Map<String, Object> getEngagementTrends() { return new HashMap<>(engagementTrends); }
    public Map<String, Object> getAudienceInsights() { return new HashMap<>(audienceInsights); }
    public LocalDateTime getGeneratedAt() { return generatedAt; }
    public LocalDateTime getScheduledAt() { return scheduledAt; }
    public String getGeneratedBy() { return generatedBy; }
    public String getReportUrl() { return reportUrl; }
    public String getReportFormat() { return reportFormat; }
    public Long getReportSize() { return reportSize; }
    public String getErrorMessage() { return errorMessage; }
    public Integer getRetryCount() { return retryCount; }
    public LocalDateTime getLastRetryAt() { return lastRetryAt; }
    public boolean isRecurring() { return isRecurring; }
    public String getRecurringSchedule() { return recurringSchedule; }
    public LocalDateTime getNextGenerationAt() { return nextGenerationAt; }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;
        AnalyticsReport that = (AnalyticsReport) o;
        return Objects.equals(companyId, that.companyId) &&
               Objects.equals(reportName, that.reportName) &&
               Objects.equals(startDate, that.startDate) &&
               Objects.equals(endDate, that.endDate);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), companyId, reportName, startDate, endDate);
    }
    
    @Override
    public String toString() {
        return "AnalyticsReport{" +
               "id=" + getId() +
               ", companyId=" + companyId +
               ", reportName='" + reportName + '\'' +
               ", reportType=" + reportType +
               ", status=" + status +
               ", startDate=" + startDate +
               ", endDate=" + endDate +
               ", generatedAt=" + generatedAt +
               '}';
    }
}
