package com.social.media.domain.socialaccount.entities;

import com.social.media.domain.company.valueobjects.CompanyId;
import com.social.media.domain.shared.entities.BaseEntity;
import com.social.media.domain.shared.valueobjects.Text;
import com.social.media.domain.socialaccount.valueobjects.SocialAccountId;
import com.social.media.domain.socialaccount.valueobjects.SocialPlatform;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.Instant;
import java.util.Objects;

/**
 * SocialAccount aggregate root.
 * 
 * Represents a social media account connected to the platform.
 * Each account belongs to a company and is associated with a specific platform.
 * 
 * @author Social Media Manager Team
 * @since 2.0.0
 */
@Entity
@Table(name = "social_accounts", schema = "core_business")
@Getter
@Setter
@NoArgsConstructor
public class SocialAccount extends BaseEntity {
    
    @Column(name = "account_uuid", nullable = false, unique = true, updatable = false)
    private String accountUuid;
    
    @Column(name = "company_id", nullable = false)
    private Long companyId;
    
    @Embedded
    @AttributeOverride(name = "value", column = @Column(name = "account_name", nullable = false, length = 255))
    private Text accountName;
    
    @Column(name = "platform_account_id", nullable = false, length = 255)
    private String platformAccountId;
    
    @Column(name = "platform_username", nullable = false, length = 255)
    private String platformUsername;
    
    @Embedded
    private SocialPlatform platform;
    
    @Column(name = "access_token", length = 1000)
    private String accessToken;
    
    @Column(name = "refresh_token", length = 1000)
    private String refreshToken;
    
    @Column(name = "token_expires_at")
    private Instant tokenExpiresAt;
    
    @Column(name = "profile_picture_url", length = 500)
    private String profilePictureUrl;
    
    @Column(name = "follower_count")
    private Long followerCount;
    
    @Column(name = "following_count")
    private Long followingCount;
    
    @Column(name = "posts_count")
    private Long postsCount;
    
    @Column(name = "is_verified", nullable = false)
    private Boolean isVerified = false;
    
    @Column(name = "is_business_account", nullable = false)
    private Boolean isBusinessAccount = false;
    
    @Column(name = "account_status", nullable = false, length = 20)
    @Enumerated(EnumType.STRING)
    private AccountStatus accountStatus = AccountStatus.CONNECTED;
    
    @Column(name = "last_sync_at")
    private Instant lastSyncAt;
    
    @Column(name = "sync_error_count", nullable = false)
    private Integer syncErrorCount = 0;
    
    @Column(name = "last_sync_error", length = 1000)
    private String lastSyncError;
    
    @Column(name = "webhook_verified", nullable = false)
    private Boolean webhookVerified = false;
    
    @Column(name = "webhook_url", length = 500)
    private String webhookUrl;
    
    @Column(name = "connected_by_user_id")
    private Long connectedByUserId;
    
    @Column(name = "connected_at")
    private Instant connectedAt;
    
    @Column(name = "disconnected_at")
    private Instant disconnectedAt;
    
    public enum AccountStatus {
        CONNECTED("Connected and active"),
        DISCONNECTED("Disconnected by user"),
        TOKEN_EXPIRED("Access token expired"),
        TOKEN_REVOKED("Access token revoked"),
        RATE_LIMITED("Rate limited by platform"),
        SUSPENDED("Account suspended"),
        ERROR("Connection error");
        
        private final String description;
        
        AccountStatus(String description) {
            this.description = description;
        }
        
        public String getDescription() {
            return description;
        }
    }
    
    // Factory method for creating new social accounts
    public static SocialAccount create(Long companyId, String accountName, String platformAccountId, 
                                     String platformUsername, SocialPlatform.PlatformType platformType,
                                     String accessToken, String refreshToken, Instant tokenExpiresAt,
                                     Long connectedByUserId) {
        SocialAccount account = new SocialAccount();
        account.accountUuid = SocialAccountId.newSocialAccountId().getStringValue();
        account.companyId = Objects.requireNonNull(companyId, "Company ID cannot be null");
        account.accountName = Text.shortText(accountName);
        account.platformAccountId = Objects.requireNonNull(platformAccountId, "Platform account ID cannot be null");
        account.platformUsername = Objects.requireNonNull(platformUsername, "Platform username cannot be null");
        account.platform = SocialPlatform.of(platformType);
        account.accessToken = accessToken;
        account.refreshToken = refreshToken;
        account.tokenExpiresAt = tokenExpiresAt;
        account.connectedByUserId = connectedByUserId;
        account.connectedAt = Instant.now();
        account.accountStatus = AccountStatus.CONNECTED;
        
        return account;
    }
    
    /**
     * Get the account ID as SocialAccountId value object
     */
    public SocialAccountId getSocialAccountId() {
        return SocialAccountId.of(accountUuid);
    }
    
    /**
     * Get the company ID as CompanyId value object
     */
    public CompanyId getCompanyId() {
        return CompanyId.of(String.valueOf(companyId));
    }
    
    /**
     * Update account basic information
     */
    public void updateAccountInfo(String accountName, String platformUsername, String profilePictureUrl) {
        this.accountName = Text.shortText(accountName);
        this.platformUsername = Objects.requireNonNull(platformUsername, "Platform username cannot be null");
        this.profilePictureUrl = profilePictureUrl;
    }
    
    /**
     * Update account statistics
     */
    public void updateStatistics(Long followerCount, Long followingCount, Long postsCount) {
        this.followerCount = followerCount;
        this.followingCount = followingCount;
        this.postsCount = postsCount;
        this.lastSyncAt = Instant.now();
    }
    
    /**
     * Update account verification status
     */
    public void updateVerificationStatus(Boolean isVerified, Boolean isBusinessAccount) {
        this.isVerified = isVerified != null ? isVerified : false;
        this.isBusinessAccount = isBusinessAccount != null ? isBusinessAccount : false;
    }
    
    /**
     * Update access tokens
     */
    public void updateTokens(String accessToken, String refreshToken, Instant tokenExpiresAt) {
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
        this.tokenExpiresAt = tokenExpiresAt;
        
        // Reset error count and status if tokens are updated successfully
        if (accessToken != null) {
            this.syncErrorCount = 0;
            this.lastSyncError = null;
            if (this.accountStatus == AccountStatus.TOKEN_EXPIRED || this.accountStatus == AccountStatus.TOKEN_REVOKED) {
                this.accountStatus = AccountStatus.CONNECTED;
            }
        }
    }
    
    /**
     * Record successful sync
     */
    public void recordSuccessfulSync() {
        this.lastSyncAt = Instant.now();
        this.syncErrorCount = 0;
        this.lastSyncError = null;
        
        if (this.accountStatus == AccountStatus.ERROR || this.accountStatus == AccountStatus.RATE_LIMITED) {
            this.accountStatus = AccountStatus.CONNECTED;
        }
    }
    
    /**
     * Record sync error
     */
    public void recordSyncError(String error) {
        this.syncErrorCount++;
        this.lastSyncError = error;
        this.lastSyncAt = Instant.now();
        
        // Determine status based on error type
        if (error.toLowerCase().contains("token")) {
            if (error.toLowerCase().contains("expired")) {
                this.accountStatus = AccountStatus.TOKEN_EXPIRED;
            } else if (error.toLowerCase().contains("revoked")) {
                this.accountStatus = AccountStatus.TOKEN_REVOKED;
            }
        } else if (error.toLowerCase().contains("rate limit")) {
            this.accountStatus = AccountStatus.RATE_LIMITED;
        } else {
            this.accountStatus = AccountStatus.ERROR;
        }
    }
    
    /**
     * Disconnect account
     */
    public void disconnect() {
        this.accountStatus = AccountStatus.DISCONNECTED;
        this.disconnectedAt = Instant.now();
        this.accessToken = null;
        this.refreshToken = null;
        this.tokenExpiresAt = null;
    }
    
    /**
     * Reconnect account
     */
    public void reconnect(String accessToken, String refreshToken, Instant tokenExpiresAt) {
        this.accountStatus = AccountStatus.CONNECTED;
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
        this.tokenExpiresAt = tokenExpiresAt;
        this.disconnectedAt = null;
        this.syncErrorCount = 0;
        this.lastSyncError = null;
        this.connectedAt = Instant.now();
    }
    
    /**
     * Check if account is connected and operational
     */
    public boolean isConnected() {
        return accountStatus == AccountStatus.CONNECTED && isActive();
    }
    
    /**
     * Check if token is expired
     */
    public boolean isTokenExpired() {
        return tokenExpiresAt != null && tokenExpiresAt.isBefore(Instant.now());
    }
    
    /**
     * Check if account needs token refresh
     */
    public boolean needsTokenRefresh() {
        // Refresh token 1 hour before expiration
        return tokenExpiresAt != null && 
               tokenExpiresAt.isBefore(Instant.now().plusSeconds(60 * 60));
    }
    
    /**
     * Check if account has too many sync errors
     */
    public boolean hasTooManyErrors() {
        return syncErrorCount >= 10;
    }
    
    /**
     * Check if account can post now
     */
    public boolean canPostNow(int postsInLastHour, int postsToday) {
        if (!isConnected()) {
            return false;
        }
        
        if (isTokenExpired()) {
            return false;
        }
        
        return platform.canPostNow(postsInLastHour, postsToday);
    }
    
    /**
     * Set webhook configuration
     */
    public void configureWebhook(String webhookUrl, Boolean verified) {
        this.webhookUrl = webhookUrl;
        this.webhookVerified = verified != null ? verified : false;
    }
    
    /**
     * Get display name for UI
     */
    public String getDisplayName() {
        return String.format("%s (@%s)", accountName.getValue(), platformUsername);
    }
    
    /**
     * Get platform display information
     */
    public String getPlatformDisplay() {
        return platform.getPlatformType().getDisplayName();
    }
    
    @Override
    public String toString() {
        return String.format("SocialAccount{id=%d, uuid='%s', platform=%s, username='%s', status=%s, active=%s}", 
            getId(), accountUuid, platform.getPlatformType(), platformUsername, accountStatus, isActive());
    }
}
