package com.social.media.domain.user;

import com.social.media.domain.company.Company;
import com.social.media.domain.company.Email;
import com.social.media.domain.shared.BaseEntity;
import com.social.media.domain.shared.enums.UserStatus;
import com.social.media.domain.shared.enums.UserType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * User entity representing a person who can access the Social Media Manager platform.
 * 
 * A User belongs to a Company and has specific permissions and access levels
 * based on their UserType. Users can have hierarchical relationships and
 * custom configurations.
 * 
 * @author Social Media Manager Team
 * @version 2.0
 * @since 2025-01-01
 */
@Getter
@Setter(AccessLevel.PRIVATE)
public class User extends BaseEntity {
    
    private String userCode;
    private UUID companyId;
    private UUID parentUserId;
    
    // Basic information
    private String name;
    private Email email;
    private String passwordHash;
    private Cpf cpf;
    private String phone;
    private String avatarUrl;
    private String position;
    private String department;
    
    // User type and status
    private UserType type;
    private UserStatus status;
    
    // Permissions and configuration
    private UserPermissions permissions;
    private UserConfiguration configuration;
    
    // Feature flags
    private boolean whatsappEnabled;
    private boolean emailVerified;
    private boolean twoFactorEnabled;
    
    // Lifecycle dates
    private Instant registrationDate;
    private Instant lastAccessDate;
    private Instant emailVerifiedAt;
    private Instant passwordChangedAt;
    
    /**
     * Default constructor for JPA and frameworks.
     */
    protected User() {
        super();
    }
    
    /**
     * Creates a new User with required information.
     * 
     * @param id the user identifier
     * @param companyId the company this user belongs to
     * @param name the user's full name
     * @param email the user's email address
     * @param type the user type/role
     */
    public User(UUID id, UUID companyId, String name, String email, UserType type) {
        super(id);
        
        validateNotNull(companyId, "Company ID");
        validateNotEmpty(name, "User name");
        validateNotEmpty(email, "User email");
        validateNotNull(type, "User type");
        
        this.companyId = companyId;
        this.name = name.trim();
        this.email = new Email(email);
        this.type = type;
        this.status = UserStatus.PENDING;
        this.registrationDate = Instant.now();
        
        // Set default permissions based on user type
        this.permissions = UserPermissions.forUserType(type);
        
        // Set default configuration
        this.configuration = UserConfiguration.defaultConfiguration();
        
        // Generate user code (will be set by infrastructure)
        this.userCode = "USER" + String.format("%06d", 0); // Placeholder
    }
    
    /**
     * Creates a new User with full information.
     */
    public User(UUID id, UUID companyId, String name, String email, UserType type,
                String cpf, String phone, String position, String department) {
        this(id, companyId, name, email, type);
        
        if (cpf != null && !cpf.trim().isEmpty()) {
            this.cpf = new Cpf(cpf);
        }
        
        this.phone = trimOrNull(phone);
        this.position = trimOrNull(position);
        this.department = trimOrNull(department);
    }
    
    // Business methods
    
    /**
     * Updates the user's basic information.
     */
    public void updateBasicInfo(String name, String phone, String position, String department) {
        if (name != null && !name.trim().isEmpty()) {
            this.name = name.trim();
        }
        
        this.phone = trimOrNull(phone);
        this.position = trimOrNull(position);
        this.department = trimOrNull(department);
        
        touch();
    }
    
    /**
     * Updates the user's email address.
     */
    public void updateEmail(String newEmail) {
        validateNotEmpty(newEmail, "Email");
        this.email = new Email(newEmail);
        this.emailVerified = false;
        this.emailVerifiedAt = null;
        touch();
    }
    
    /**
     * Updates the user's CPF.
     */
    public void updateCpf(String newCpf) {
        if (newCpf != null && !newCpf.trim().isEmpty()) {
            this.cpf = new Cpf(newCpf);
        } else {
            this.cpf = null;
        }
        touch();
    }
    
    /**
     * Updates the user's avatar URL.
     */
    public void updateAvatarUrl(String avatarUrl) {
        this.avatarUrl = trimOrNull(avatarUrl);
        touch();
    }
    
    /**
     * Changes the user's type/role.
     */
    public void changeType(UserType newType) {
        validateNotNull(newType, "New user type");
        
        if (this.type == newType) {
            return; // No change needed
        }
        
        this.type = newType;
        // Update permissions to match new type
        this.permissions = UserPermissions.forUserType(newType);
        touch();
    }
    
    /**
     * Changes the user's status.
     */
    public void changeStatus(UserStatus newStatus) {
        validateNotNull(newStatus, "New status");
        
        if (!this.status.canTransitionTo(newStatus)) {
            throw new IllegalArgumentException("Cannot transition from " + 
                                             this.status + " to " + newStatus);
        }
        
        this.status = newStatus;
        touch();
    }
    
    /**
     * Activates the user account.
     */
    public void activate() {
        changeStatus(UserStatus.ACTIVE);
    }
    
    /**
     * Deactivates the user account.
     */
    public void deactivate() {
        changeStatus(UserStatus.INACTIVE);
    }
    
    /**
     * Suspends the user account.
     */
    public void suspend() {
        changeStatus(UserStatus.SUSPENDED);
    }
    
    /**
     * Sets the password hash for the user.
     */
    public void setPasswordHash(String passwordHash) {
        validateNotEmpty(passwordHash, "Password hash");
        this.passwordHash = passwordHash;
        this.passwordChangedAt = Instant.now();
        touch();
    }
    
    /**
     * Verifies the user's email address.
     */
    public void verifyEmail() {
        this.emailVerified = true;
        this.emailVerifiedAt = Instant.now();
        
        // If user was pending and email is verified, activate them
        if (this.status == UserStatus.PENDING) {
            this.status = UserStatus.ACTIVE;
        }
        
        touch();
    }
    
    /**
     * Enables two-factor authentication.
     */
    public void enableTwoFactor() {
        this.twoFactorEnabled = true;
        touch();
    }
    
    /**
     * Disables two-factor authentication.
     */
    public void disableTwoFactor() {
        this.twoFactorEnabled = false;
        touch();
    }
    
    /**
     * Enables WhatsApp notifications.
     */
    public void enableWhatsapp() {
        this.whatsappEnabled = true;
        touch();
    }
    
    /**
     * Disables WhatsApp notifications.
     */
    public void disableWhatsapp() {
        this.whatsappEnabled = false;
        touch();
    }
    
    /**
     * Updates the user's permissions.
     */
    public void updatePermissions(UserPermissions newPermissions) {
        validateNotNull(newPermissions, "Permissions");
        this.permissions = newPermissions;
        touch();
    }
    
    /**
     * Updates the user's configuration.
     */
    public void updateConfiguration(UserConfiguration newConfiguration) {
        validateNotNull(newConfiguration, "Configuration");
        this.configuration = newConfiguration;
        touch();
    }
    
    /**
     * Sets the parent user (for hierarchical relationships).
     */
    public void setParentUser(UUID parentUserId) {
        if (parentUserId != null && parentUserId.equals(this.getId())) {
            throw new IllegalArgumentException("User cannot be their own parent");
        }
        this.parentUserId = parentUserId;
        touch();
    }
    
    /**
     * Records user access.
     */
    public void recordAccess() {
        this.lastAccessDate = Instant.now();
        touch();
    }
    
    // Query methods
    
    /**
     * Checks if the user can login.
     */
    public boolean canLogin() {
        return status.canLogin() && emailVerified;
    }
    
    /**
     * Checks if the user can access system features.
     */
    public boolean canAccessFeatures() {
        return status.canAccessFeatures();
    }
    
    /**
     * Checks if the user needs email verification.
     */
    public boolean needsEmailVerification() {
        return !emailVerified && status == UserStatus.PENDING;
    }
    
    /**
     * Checks if the user has a parent user.
     */
    public boolean hasParent() {
        return parentUserId != null;
    }
    
    /**
     * Checks if the user can manage another user.
     */
    public boolean canManage(User otherUser) {
        // Same company check
        if (!this.companyId.equals(otherUser.companyId)) {
            return false;
        }
        
        // Permission check
        if (!this.permissions.isCanManageUsers()) {
            return false;
        }
        
        // Type hierarchy check
        return this.type.canManage(otherUser.type);
    }
    
    /**
     * Checks if the user belongs to the same company as another user.
     */
    public boolean isSameCompany(User otherUser) {
        return this.companyId.equals(otherUser.companyId);
    }
    
    /**
     * Gets the user's display name (name or email if name is not available).
     */
    public String getDisplayName() {
        return name != null && !name.trim().isEmpty() ? name : email.getValue();
    }
    
    /**
     * Gets the user's initials for avatar display.
     */
    public String getInitials() {
        if (name == null || name.trim().isEmpty()) {
            return email.getValue().substring(0, 2).toUpperCase();
        }
        
        String[] nameParts = name.trim().split("\\s+");
        if (nameParts.length == 1) {
            return nameParts[0].substring(0, Math.min(2, nameParts[0].length())).toUpperCase();
        }
        
        return (nameParts[0].substring(0, 1) + 
                nameParts[nameParts.length - 1].substring(0, 1)).toUpperCase();
    }
    
    // Private helper methods
    
    private String trimOrNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }
    
    private void validateNotEmpty(String value, String fieldName) {
        if (value == null || value.trim().isEmpty()) {
            throw new IllegalArgumentException(fieldName + " cannot be null or empty");
        }
    }
    
    private void validateNotNull(Object value, String fieldName) {
        if (value == null) {
            throw new IllegalArgumentException(fieldName + " cannot be null");
        }
    }
    
    @Override
    public void validate() {
        super.validate();
        
        validateNotNull(companyId, "Company ID");
        validateNotEmpty(name, "User name");
        validateNotNull(email, "User email");
        validateNotNull(type, "User type");
        validateNotNull(status, "User status");
        validateNotNull(permissions, "User permissions");
        validateNotNull(configuration, "User configuration");
        
        if (parentUserId != null && parentUserId.equals(getId())) {
            throw new IllegalStateException("User cannot be their own parent");
        }
        
        if (passwordChangedAt != null && registrationDate != null &&
            passwordChangedAt.isBefore(registrationDate)) {
            throw new IllegalStateException("Password change date cannot be before registration date");
        }
        
        if (emailVerifiedAt != null && registrationDate != null &&
            emailVerifiedAt.isBefore(registrationDate)) {
            throw new IllegalStateException("Email verification date cannot be before registration date");
        }
    }
    
    @Override
    public String toString() {
        return "User{" +
               "id=" + getId() +
               ", userCode='" + userCode + '\'' +
               ", companyId=" + companyId +
               ", name='" + name + '\'' +
               ", email=" + email +
               ", type=" + type +
               ", status=" + status +
               '}';
    }
}
