package com.social.media.domain.company;

import com.social.media.domain.shared.BaseValueObject;
import lombok.EqualsAndHashCode;
import lombok.Getter;

import java.util.Objects;
import java.util.regex.Pattern;

/**
 * Value Object representing an email address.
 * 
 * Ensures that email addresses are valid according to basic RFC 5322 standards.
 * Provides normalization and validation for email addresses used throughout the system.
 * 
 * @author Social Media Manager Team
 * @version 2.0
 * @since 2025-01-01
 */
@Getter
@EqualsAndHashCode(callSuper = false)
public final class Email extends BaseValueObject {
    
    private static final Pattern EMAIL_PATTERN = Pattern.compile(
        "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"
    );
    
    private static final int MAX_LENGTH = 320; // RFC 5321 limit
    private static final int MAX_LOCAL_LENGTH = 64; // Local part limit
    private static final int MAX_DOMAIN_LENGTH = 253; // Domain part limit
    
    private final String value;
    private final String localPart;
    private final String domainPart;
    
    /**
     * Creates a new Email from a string value.
     * 
     * @param value the email string
     * @throws IllegalArgumentException if the email is invalid
     */
    public Email(String value) {
        validateNotNull(value, "Email");
        validateNotEmpty(value.trim(), "Email");
        
        // Normalize email (lowercase and trim)
        String normalizedValue = value.trim().toLowerCase();
        
        this.value = normalizedValue;
        
        // Split into local and domain parts
        String[] parts = normalizedValue.split("@");
        if (parts.length != 2) {
            throw new IllegalArgumentException("Email must contain exactly one @ symbol");
        }
        
        this.localPart = parts[0];
        this.domainPart = parts[1];
        
        super.validate(); // This will call our validate() method
    }
    
    @Override
    public void validate() {
        // Check overall length
        if (value.length() > MAX_LENGTH) {
            throw new IllegalArgumentException("Email cannot exceed " + MAX_LENGTH + " characters");
        }
        
        // Check local part length
        if (localPart.length() > MAX_LOCAL_LENGTH) {
            throw new IllegalArgumentException("Email local part cannot exceed " + MAX_LOCAL_LENGTH + " characters");
        }
        
        // Check domain part length
        if (domainPart.length() > MAX_DOMAIN_LENGTH) {
            throw new IllegalArgumentException("Email domain part cannot exceed " + MAX_DOMAIN_LENGTH + " characters");
        }
        
        // Check format using regex
        if (!EMAIL_PATTERN.matcher(value).matches()) {
            throw new IllegalArgumentException("Invalid email format");
        }
        
        // Additional validation rules
        validateLocalPart();
        validateDomainPart();
    }
    
    /**
     * Gets the complete email address.
     * 
     * @return the email address
     */
    public String getValue() {
        return value;
    }
    
    /**
     * Gets the local part of the email (before @).
     * 
     * @return the local part
     */
    public String getLocalPart() {
        return localPart;
    }
    
    /**
     * Gets the domain part of the email (after @).
     * 
     * @return the domain part
     */
    public String getDomainPart() {
        return domainPart;
    }
    
    /**
     * Gets the domain name without subdomain.
     * 
     * @return the root domain name
     */
    public String getRootDomain() {
        String[] domainParts = domainPart.split("\\.");
        if (domainParts.length >= 2) {
            return domainParts[domainParts.length - 2] + "." + domainParts[domainParts.length - 1];
        }
        return domainPart;
    }
    
    /**
     * Checks if this email belongs to a business domain (not free email providers).
     * 
     * @return true if this appears to be a business email
     */
    public boolean isBusinessEmail() {
        String rootDomain = getRootDomain().toLowerCase();
        
        // Common free email providers
        String[] freeProviders = {
            "gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "live.com",
            "icloud.com", "aol.com", "protonmail.com", "zoho.com", "mail.com",
            "uol.com.br", "bol.com.br", "ig.com.br", "terra.com.br"
        };
        
        for (String provider : freeProviders) {
            if (rootDomain.equals(provider)) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Creates a masked version of the email for display purposes.
     * Example: john.doe@example.com becomes j***e@e***e.com
     * 
     * @return masked email string
     */
    public String getMasked() {
        if (localPart.length() <= 2) {
            return localPart.charAt(0) + "***@" + maskDomain();
        }
        
        String maskedLocal = localPart.charAt(0) + 
                            "*".repeat(Math.max(1, localPart.length() - 2)) + 
                            localPart.charAt(localPart.length() - 1);
        
        return maskedLocal + "@" + maskDomain();
    }
    
    /**
     * Validates if a string represents a valid email address.
     * 
     * @param emailString the email string to validate
     * @return true if the email is valid
     */
    public static boolean isValid(String emailString) {
        try {
            new Email(emailString);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }
    
    /**
     * Creates an Email instance if the string is valid, otherwise returns null.
     * 
     * @param emailString the email string
     * @return Email instance or null if invalid
     */
    public static Email of(String emailString) {
        try {
            return new Email(emailString);
        } catch (IllegalArgumentException e) {
            return null;
        }
    }
    
    /**
     * Validates the local part of the email.
     */
    private void validateLocalPart() {
        if (localPart.isEmpty()) {
            throw new IllegalArgumentException("Email local part cannot be empty");
        }
        
        if (localPart.startsWith(".") || localPart.endsWith(".")) {
            throw new IllegalArgumentException("Email local part cannot start or end with a dot");
        }
        
        if (localPart.contains("..")) {
            throw new IllegalArgumentException("Email local part cannot contain consecutive dots");
        }
    }
    
    /**
     * Validates the domain part of the email.
     */
    private void validateDomainPart() {
        if (domainPart.isEmpty()) {
            throw new IllegalArgumentException("Email domain part cannot be empty");
        }
        
        if (domainPart.startsWith(".") || domainPart.endsWith(".")) {
            throw new IllegalArgumentException("Email domain part cannot start or end with a dot");
        }
        
        if (domainPart.contains("..")) {
            throw new IllegalArgumentException("Email domain part cannot contain consecutive dots");
        }
        
        if (!domainPart.contains(".")) {
            throw new IllegalArgumentException("Email domain must contain at least one dot");
        }
    }
    
    /**
     * Creates a masked version of the domain part.
     * 
     * @return masked domain string
     */
    private String maskDomain() {
        String[] parts = domainPart.split("\\.");
        if (parts.length == 0) {
            return "***";
        }
        
        StringBuilder masked = new StringBuilder();
        for (int i = 0; i < parts.length; i++) {
            if (i > 0) {
                masked.append(".");
            }
            
            String part = parts[i];
            if (part.length() <= 2) {
                masked.append(part.charAt(0)).append("*");
            } else {
                masked.append(part.charAt(0))
                      .append("*".repeat(Math.max(1, part.length() - 2)))
                      .append(part.charAt(part.length() - 1));
            }
        }
        
        return masked.toString();
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Email email = (Email) obj;
        return Objects.equals(value, email.value);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(value);
    }
    
    @Override
    public String toString() {
        return value;
    }
}
