package com.social.media.domain.user.entities;

import com.social.media.domain.company.valueobjects.CompanyId;
import com.social.media.domain.shared.AggregateRoot;
import com.social.media.domain.shared.valueobjects.Email;
import com.social.media.domain.shared.valueobjects.Text;
import com.social.media.domain.user.valueobjects.UserId;
import com.social.media.domain.user.valueobjects.UserRole;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;
import java.time.Instant;
import java.util.Objects;

/**
 * User Aggregate Root
 * 
 * Represents a user of the social media management platform.
 * Users belong to companies and have roles that define their permissions.
 * 
 * Based on Database Schema: core_business.users
 * 
 * @author Social Media Manager Team
 * @since 2.0.0
 */
@Entity
@Table(name = "users", schema = "core_business", 
       indexes = {
           @Index(name = "idx_users_uuid", columnList = "user_uuid"),
           @Index(name = "idx_users_company", columnList = "company_id"),
           @Index(name = "idx_users_email", columnList = "email"),
           @Index(name = "idx_users_role", columnList = "role"),
           @Index(name = "idx_users_status", columnList = "status")
       })
@Getter
@Setter(AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User extends AggregateRoot<UserId> {
    
    // Basic Information
    @Column(name = "user_uuid", nullable = false, unique = true, updatable = false, length = 36)
    private String userUuid;
    
    @Column(name = "company_id", nullable = false)
    private Long companyId;
    
    @Embedded
    @AttributeOverride(name = "value", column = @Column(name = "first_name", nullable = false, length = 100))
    private Text firstName;
    
    @Embedded
    @AttributeOverride(name = "value", column = @Column(name = "last_name", nullable = false, length = 100))
    private Text lastName;
    
    @Embedded
    @AttributeOverride(name = "value", column = @Column(name = "email", nullable = false, unique = true))
    private Email email;
    
    @Column(name = "password_hash", nullable = false, length = 255)
    private String passwordHash;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "role", nullable = false)
    private UserRole.RoleType role = UserRole.RoleType.VIEWER;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "status", nullable = false)
    private UserStatus status = UserStatus.PENDING;
    
    // Profile Information
    @Column(name = "avatar_url", length = 500)
    private String avatarUrl;
    
    @Column(name = "phone", length = 20)
    private String phone;
    
    @Column(name = "timezone", length = 50)
    private String timezone = "America/Sao_Paulo";
    
    @Column(name = "locale", length = 10)
    private String locale = "pt_BR";
    
    // Authentication & Security
    @Column(name = "email_verified", nullable = false)
    private Boolean emailVerified = false;
    
    @Column(name = "email_verification_token", length = 255)
    private String emailVerificationToken;
    
    @Column(name = "password_reset_token", length = 255)
    private String passwordResetToken;
    
    @Column(name = "password_reset_expires_at")
    private LocalDateTime passwordResetExpiresAt;
    
    @Column(name = "last_login_at")
    private LocalDateTime lastLoginAt;
    
    @Column(name = "login_attempts", nullable = false)
    private Integer loginAttempts = 0;
    
    @Column(name = "locked_until")
    private LocalDateTime lockedUntil;
    
    // Preferences
    @Column(name = "two_factor_enabled", nullable = false)
    private Boolean twoFactorEnabled = false;
    
    @Column(name = "notifications_enabled", nullable = false)
    private Boolean notificationsEnabled = true;
    
    @Column(name = "email_notifications", nullable = false)
    private Boolean emailNotifications = true;
    
    public enum UserStatus {
        PENDING,     // Account created, pending email verification
        ACTIVE,      // Active and verified user
        INACTIVE,    // Temporarily inactive
        SUSPENDED,   // Suspended by admin
        LOCKED,      // Locked due to failed login attempts
        ARCHIVED     // Archived/soft deleted
    }
    
    /**
     * Constructor for creating User with specific ID
     */
    protected User(UserId id) {
        super(id);
        this.userUuid = id.getValueAsString();
    }
    
    /**
     * Post-load hook to initialize from database
     */
    @PostLoad
    protected void initializeFromDatabase() {
        if (this.id != null && this.userUuid == null) {
            this.userUuid = this.id.getValueAsString();
        }
    }
    
    // Factory Methods
    
    /**
     * Create a new user with basic information
     */
    public static User create(CompanyId companyId, String firstName, String lastName, 
                             String email, String passwordHash, UserRole.RoleType role) {
        var userId = UserId.newUserId();
        var user = new User(userId);
        
        user.companyId = Objects.requireNonNull(companyId, "Company ID is required").getValueAsLong();
        user.firstName = Text.of(firstName);
        user.lastName = Text.of(lastName);
        user.email = Email.of(email);
        user.passwordHash = Objects.requireNonNull(passwordHash, "Password hash is required");
        user.role = Objects.requireNonNull(role, "Role is required");
        user.status = UserStatus.PENDING;
        
        // TODO: Generate email verification token
        // TODO: Publish UserCreated domain event
        
        return user;
    }
    
    // Business Logic Methods
    
    /**
     * Get user ID as value object
     */
    public UserId getUserId() {
        return getId();
    }
    
    /**
     * Get user UUID as string
     */
    public String getUserUuid() {
        return userUuid;
    }
    
    /**
     * Get company ID as value object
     */
    public CompanyId getCompanyId() {
        return CompanyId.of(companyId);
    }
    
    /**
     * Get company ID as Long
     */
    public Long getCompanyIdAsLong() {
        return companyId;
    }
    
    /**
     * Get full name
     */
    public String getFullName() {
        return firstName.getValue() + " " + lastName.getValue();
    }
    
    /**
     * Update basic profile information
     */
    public void updateProfile(String firstName, String lastName, String phone, 
                             String timezone, String locale) {
        this.firstName = Text.of(firstName);
        this.lastName = Text.of(lastName);
        this.phone = phone;
        this.timezone = timezone != null ? timezone : this.timezone;
        this.locale = locale != null ? locale : this.locale;
        markAsModified();
    }
    
    /**
     * Update avatar URL
     */
    public void updateAvatar(String avatarUrl) {
        this.avatarUrl = avatarUrl;
        markAsModified();
    }
    
    /**
     * Change user role (admin only)
     */
    public void changeRole(UserRole.RoleType newRole) {
        var oldRole = this.role;
        this.role = newRole;
        // TODO: Publish UserRoleChanged domain event
        markAsModified();
    }
    
    /**
     * Verify email address
     */
    public void verifyEmail(String token) {
        if (!Objects.equals(this.emailVerificationToken, token)) {
            throw new IllegalArgumentException("Invalid verification token");
        }
        
        this.emailVerified = true;
        this.emailVerificationToken = null;
        this.status = UserStatus.ACTIVE;
        
        // TODO: Publish EmailVerified domain event
        markAsModified();
    }
    
    /**
     * Change password
     */
    public void changePassword(String newPasswordHash) {
        this.passwordHash = Objects.requireNonNull(newPasswordHash, "Password hash is required");
        this.passwordResetToken = null;
        this.passwordResetExpiresAt = null;
        markAsModified();
    }
    
    /**
     * Generate password reset token
     */
    public void generatePasswordResetToken(String token, LocalDateTime expiresAt) {
        this.passwordResetToken = token;
        this.passwordResetExpiresAt = expiresAt;
        markAsModified();
    }
    
    /**
     * Record successful login
     */
    public void recordSuccessfulLogin() {
        this.lastLoginAt = LocalDateTime.now();
        this.loginAttempts = 0;
        this.lockedUntil = null;
        markAsModified();
    }
    
    /**
     * Record failed login attempt
     */
    public void recordFailedLogin() {
        this.loginAttempts++;
        
        // Lock account after 5 failed attempts for 30 minutes
        if (this.loginAttempts >= 5) {
            this.lockedUntil = LocalDateTime.now().plusMinutes(30);
            this.status = UserStatus.LOCKED;
        }
        
        markAsModified();
    }
    
    /**
     * Suspend user (admin action)
     */
    public void suspend(String reason) {
        this.status = UserStatus.SUSPENDED;
        // TODO: Publish UserSuspended domain event with reason
        markAsModified();
    }
    
    /**
     * Reactivate suspended user
     */
    public void reactivate() {
        if (status != UserStatus.SUSPENDED) {
            throw new IllegalStateException("Only suspended users can be reactivated");
        }
        
        this.status = UserStatus.ACTIVE;
        // TODO: Publish UserReactivated domain event
        markAsModified();
    }
    
    /**
     * Archive user (soft delete)
     */
    public void archive() {
        this.status = UserStatus.ARCHIVED;
        // TODO: Publish UserArchived domain event
        markAsModified();
    }
    
    /**
     * Update notification preferences
     */
    public void updateNotificationPreferences(Boolean notificationsEnabled, 
                                            Boolean emailNotifications) {
        this.notificationsEnabled = notificationsEnabled;
        this.emailNotifications = emailNotifications;
        markAsModified();
    }
    
    /**
     * Enable/disable two-factor authentication
     */
    public void setTwoFactorAuthentication(Boolean enabled) {
        this.twoFactorEnabled = enabled;
        markAsModified();
    }
    
    // Query Methods
    
    /**
     * Check if user is active
     */
    public boolean isActive() {
        return status == UserStatus.ACTIVE && emailVerified;
    }
    
    /**
     * Check if user account is locked
     */
    public boolean isLocked() {
        return status == UserStatus.LOCKED || 
               (lockedUntil != null && lockedUntil.isAfter(LocalDateTime.now()));
    }
    
    /**
     * Check if user can login
     */
    public boolean canLogin() {
        return isActive() && !isLocked();
    }
    
    /**
     * Check if user has admin role
     */
    public boolean isAdmin() {
        return role == UserRole.RoleType.ADMIN;
    }
    
    /**
     * Check if user has manager role or higher
     */
    public boolean isManagerOrHigher() {
        return role == UserRole.RoleType.ADMIN || role == UserRole.RoleType.MANAGER;
    }
    
    /**
     * Check if password reset token is valid
     */
    public boolean isPasswordResetTokenValid(String token) {
        return Objects.equals(this.passwordResetToken, token) &&
               this.passwordResetExpiresAt != null &&
               this.passwordResetExpiresAt.isAfter(LocalDateTime.now());
    }
    
    private void markAsModified() {
        // This would trigger domain events and update audit fields
        // Implementation depends on your domain event infrastructure
    }
    
    /**
     * Check if user can perform a specific action
     */
    public boolean canPerformAction(String action) {
        if (status != UserStatus.ACTIVE) {
            return false;
        }
        
        // Simple role-based check - can be expanded as needed
        return switch (role) {
            case ADMIN -> true;
            case MANAGER -> !"DELETE_COMPANY".equals(action);
            case EDITOR -> "CREATE_POST".equals(action) || "VIEW_POSTS".equals(action);
            case VIEWER -> "VIEW_POSTS".equals(action);
            default -> false;
        };
    }
    
    /**
     * Get user email
     */
    public String getEmail() {
        return email != null ? email.getValue() : null;
    }
    
    /**
     * Get user role
     */
    public UserRole.RoleType getRole() {
        return role;
    }
    
    /**
     * Check if email is verified
     */
    public boolean getEmailVerified() {
        return emailVerified != null ? emailVerified : false;
    }

    @Override
    public String toString() {
        return String.format("User{id=%s, uuid='%s', email='%s', role=%s, status=%s}", 
            getId(), userUuid, email != null ? email.getValue() : null, role, status);
    }
}
