package com.social.media.domain.socialaccount.aggregate;

import com.social.media.domain.socialaccount.valueobject.*;
import com.social.media.domain.socialnetwork.valueobject.SocialNetworkId;
import com.social.media.domain.company.valueobject.CompanyId;
import com.social.media.domain.user.valueobject.UserId;
import com.social.media.domain.shared.valueobject.Email;

import java.time.LocalDateTime;
import java.util.Objects;

/**
 * Social Account Aggregate Root
 * Represents a social media account managed by the system
 */
public class SocialAccount {
    
    private SocialAccountId id;
    private CompanyId companyId;
    private UserId responsibleUserId;
    private SocialNetworkId socialNetworkId;
    private String username;
    private String displayName;
    private String bio;
    private String profilePhotoUrl;
    private String profileUrl;
    private AccountMetrics metrics;
    private boolean verified;
    private boolean isPrivate;
    private String category;
    private String contactPhone;
    private Email contactEmail;
    private String location;
    private String website;
    private AuthenticationTokens tokens;
    private AutomationConfiguration automationConfig;
    private ConnectionStatus connectionStatus;
    private LocalDateTime lastSyncDate;
    private LocalDateTime nextSyncDate;
    private String lastSyncError;
    private boolean active;
    private LocalDateTime registrationDate;
    private LocalDateTime lastUpdateDate;
    private boolean deleted;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    
    // Private constructor for builder pattern
    private SocialAccount() {}
    
    /**
     * Creates a new social account (factory method)
     */
    public static SocialAccount create(CompanyId companyId, SocialNetworkId socialNetworkId,
                                     String username, AuthenticationTokens tokens) {
        SocialAccount account = new SocialAccount();
        account.companyId = Objects.requireNonNull(companyId, "Company ID cannot be null");
        account.socialNetworkId = Objects.requireNonNull(socialNetworkId, "Social Network ID cannot be null");
        account.username = validateUsername(username);
        account.tokens = Objects.requireNonNull(tokens, "Authentication tokens cannot be null");
        account.metrics = AccountMetrics.empty();
        account.automationConfig = AutomationConfiguration.disabled();
        account.connectionStatus = ConnectionStatus.ACTIVE;
        account.verified = false;
        account.isPrivate = false;
        account.active = true;
        account.registrationDate = LocalDateTime.now();
        account.lastUpdateDate = LocalDateTime.now();
        account.deleted = false;
        account.createdAt = LocalDateTime.now();
        account.updatedAt = LocalDateTime.now();
        
        return account;
    }
    
    /**
     * Reconstructs social account from persistence (for repository)
     */
    public static SocialAccount reconstruct(SocialAccountId id, CompanyId companyId, UserId responsibleUserId,
                                          SocialNetworkId socialNetworkId, String username, String displayName,
                                          String bio, String profilePhotoUrl, String profileUrl,
                                          AccountMetrics metrics, boolean verified, boolean isPrivate,
                                          String category, String contactPhone, Email contactEmail,
                                          String location, String website, AuthenticationTokens tokens,
                                          AutomationConfiguration automationConfig, ConnectionStatus connectionStatus,
                                          LocalDateTime lastSyncDate, LocalDateTime nextSyncDate, String lastSyncError,
                                          boolean active, LocalDateTime registrationDate, LocalDateTime lastUpdateDate,
                                          boolean deleted, LocalDateTime createdAt, LocalDateTime updatedAt) {
        SocialAccount account = new SocialAccount();
        account.id = id;
        account.companyId = companyId;
        account.responsibleUserId = responsibleUserId;
        account.socialNetworkId = socialNetworkId;
        account.username = username;
        account.displayName = displayName;
        account.bio = bio;
        account.profilePhotoUrl = profilePhotoUrl;
        account.profileUrl = profileUrl;
        account.metrics = metrics;
        account.verified = verified;
        account.isPrivate = isPrivate;
        account.category = category;
        account.contactPhone = contactPhone;
        account.contactEmail = contactEmail;
        account.location = location;
        account.website = website;
        account.tokens = tokens;
        account.automationConfig = automationConfig;
        account.connectionStatus = connectionStatus;
        account.lastSyncDate = lastSyncDate;
        account.nextSyncDate = nextSyncDate;
        account.lastSyncError = lastSyncError;
        account.active = active;
        account.registrationDate = registrationDate;
        account.lastUpdateDate = lastUpdateDate;
        account.deleted = deleted;
        account.createdAt = createdAt;
        account.updatedAt = updatedAt;
        
        return account;
    }
    
    // Business Methods
    
    public void activate() {
        if (deleted) {
            throw new SocialAccountDomainException("Cannot activate deleted social account");
        }
        this.active = true;
        this.connectionStatus = ConnectionStatus.ACTIVE;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void deactivate() {
        this.active = false;
        this.connectionStatus = ConnectionStatus.INACTIVE;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void suspend() {
        this.connectionStatus = ConnectionStatus.SUSPENDED;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void markAsBanned() {
        this.connectionStatus = ConnectionStatus.BANNED;
        this.active = false;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void updateProfile(String displayName, String bio, String profilePhotoUrl) {
        this.displayName = displayName;
        this.bio = bio;
        this.profilePhotoUrl = profilePhotoUrl;
        this.lastUpdateDate = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
    
    public void updateContactInfo(String contactPhone, Email contactEmail, String website) {
        this.contactPhone = contactPhone;
        this.contactEmail = contactEmail;
        this.website = website;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void updateTokens(AuthenticationTokens newTokens) {
        this.tokens = Objects.requireNonNull(newTokens, "Tokens cannot be null");
        if (connectionStatus == ConnectionStatus.TOKEN_ERROR) {
            this.connectionStatus = ConnectionStatus.ACTIVE;
        }
        this.updatedAt = LocalDateTime.now();
    }
    
    public void reportTokenError(String error) {
        this.connectionStatus = ConnectionStatus.TOKEN_ERROR;
        this.lastSyncError = error;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void updateMetrics(AccountMetrics newMetrics) {
        this.metrics = Objects.requireNonNull(newMetrics, "Metrics cannot be null");
        this.lastSyncDate = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
    
    public void updateAutomationConfig(AutomationConfiguration config) {
        this.automationConfig = Objects.requireNonNull(config, "Automation config cannot be null");
        this.updatedAt = LocalDateTime.now();
    }
    
    public void assignResponsibleUser(UserId userId) {
        this.responsibleUserId = userId;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void markAsVerified() {
        this.verified = true;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void markAsPrivate() {
        this.isPrivate = true;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void markAsPublic() {
        this.isPrivate = false;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void setCategory(String category) {
        this.category = category;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void setLocation(String location) {
        this.location = location;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void softDelete() {
        this.deleted = true;
        this.active = false;
        this.connectionStatus = ConnectionStatus.INACTIVE;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void scheduleSync(LocalDateTime nextSync) {
        this.nextSyncDate = nextSync;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void recordSyncSuccess() {
        this.lastSyncDate = LocalDateTime.now();
        this.lastSyncError = null;
        if (connectionStatus == ConnectionStatus.TOKEN_ERROR) {
            this.connectionStatus = ConnectionStatus.ACTIVE;
        }
        this.updatedAt = LocalDateTime.now();
    }
    
    public void recordSyncError(String error) {
        this.lastSyncDate = LocalDateTime.now();
        this.lastSyncError = error;
        this.updatedAt = LocalDateTime.now();
    }
    
    public boolean canPublish() {
        return active && !deleted && connectionStatus.canPublish() && !tokens.isExpired();
    }
    
    public boolean canAutomateActions() {
        return canPublish() && automationConfig.hasAnyAutomationEnabled();
    }
    
    public boolean needsTokenRefresh() {
        return tokens.needsRefresh();
    }
    
    public boolean isSyncDue() {
        return nextSyncDate != null && nextSyncDate.isBefore(LocalDateTime.now());
    }
    
    public boolean belongsToCompany(CompanyId companyId) {
        return Objects.equals(this.companyId, companyId);
    }
    
    // Validation methods
    
    private static String validateUsername(String username) {
        if (username == null || username.trim().isEmpty()) {
            throw new SocialAccountDomainException("Username cannot be null or empty");
        }
        if (username.trim().length() < 1) {
            throw new SocialAccountDomainException("Username must have at least 1 character");
        }
        if (username.trim().length() > 100) {
            throw new SocialAccountDomainException("Username must not exceed 100 characters");
        }
        return username.trim();
    }
    
    // Getters
    
    public SocialAccountId getId() { return id; }
    public CompanyId getCompanyId() { return companyId; }
    public UserId getResponsibleUserId() { return responsibleUserId; }
    public SocialNetworkId getSocialNetworkId() { return socialNetworkId; }
    public String getUsername() { return username; }
    public String getDisplayName() { return displayName; }
    public String getBio() { return bio; }
    public String getProfilePhotoUrl() { return profilePhotoUrl; }
    public String getProfileUrl() { return profileUrl; }
    public AccountMetrics getMetrics() { return metrics; }
    public boolean isVerified() { return verified; }
    public boolean isPrivate() { return isPrivate; }
    public String getCategory() { return category; }
    public String getContactPhone() { return contactPhone; }
    public Email getContactEmail() { return contactEmail; }
    public String getLocation() { return location; }
    public String getWebsite() { return website; }
    public AuthenticationTokens getTokens() { return tokens; }
    public AutomationConfiguration getAutomationConfig() { return automationConfig; }
    public ConnectionStatus getConnectionStatus() { return connectionStatus; }
    public LocalDateTime getLastSyncDate() { return lastSyncDate; }
    public LocalDateTime getNextSyncDate() { return nextSyncDate; }
    public String getLastSyncError() { return lastSyncError; }
    public boolean isActive() { return active; }
    public LocalDateTime getRegistrationDate() { return registrationDate; }
    public LocalDateTime getLastUpdateDate() { return lastUpdateDate; }
    public boolean isDeleted() { return deleted; }
    public LocalDateTime getCreatedAt() { return createdAt; }
    public LocalDateTime getUpdatedAt() { return updatedAt; }
    
    // Setters for reconstruction only
    void setId(SocialAccountId id) { this.id = id; }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SocialAccount account = (SocialAccount) o;
        return Objects.equals(id, account.id);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
    
    @Override
    public String toString() {
        return "SocialAccount{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", socialNetworkId=" + socialNetworkId +
                ", connectionStatus=" + connectionStatus +
                ", active=" + active +
                '}';
    }
    
    /**
     * Domain exception specific to SocialAccount aggregate
     */
    public static class SocialAccountDomainException extends RuntimeException {
        public SocialAccountDomainException(String message) {
            super(message);
        }
    }
}
