package com.social.media.domain.company.valueobjects;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
 * CNPJ Value Object
 * 
 * Represents a Brazilian CNPJ (Cadastro Nacional da Pessoa Jurídica)
 * with validation and formatting capabilities.
 * 
 * @author Social Media Manager Team
 * @since 2.0.0
 */
@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode
public class Cnpj {
    
    @Column(name = "cnpj", length = 18, unique = true)
    private String value;
    
    private Cnpj(String value) {
        this.value = value;
    }
    
    /**
     * Create CNPJ from string value
     */
    @JsonCreator
    public static Cnpj of(String cnpj) {
        if (cnpj == null || cnpj.isBlank()) {
            throw new IllegalArgumentException("CNPJ cannot be null or empty");
        }
        
        var cleanCnpj = cleanCnpj(cnpj);
        
        if (!isValidCnpj(cleanCnpj)) {
            throw new IllegalArgumentException("Invalid CNPJ: " + cnpj);
        }
        
        return new Cnpj(formatCnpj(cleanCnpj));
    }
    
    /**
     * Get formatted CNPJ value
     */
    @JsonValue
    public String getValue() {
        return value;
    }
    
    /**
     * Get CNPJ without formatting (digits only)
     */
    public String getDigitsOnly() {
        return value != null ? value.replaceAll("[^0-9]", "") : null;
    }
    
    /**
     * Clean CNPJ string (remove all non-digit characters)
     */
    private static String cleanCnpj(String cnpj) {
        return cnpj.replaceAll("[^0-9]", "");
    }
    
    /**
     * Format CNPJ with standard Brazilian formatting
     */
    private static String formatCnpj(String cnpj) {
        if (cnpj.length() != 14) {
            return cnpj;
        }
        
        return cnpj.substring(0, 2) + "." +
               cnpj.substring(2, 5) + "." +
               cnpj.substring(5, 8) + "/" +
               cnpj.substring(8, 12) + "-" +
               cnpj.substring(12, 14);
    }
    
    /**
     * Validate CNPJ using the official algorithm
     */
    private static boolean isValidCnpj(String cnpj) {
        if (cnpj == null || cnpj.length() != 14) {
            return false;
        }
        
        // Check if all digits are the same
        if (cnpj.matches("(\\d)\\1{13}")) {
            return false;
        }
        
        try {
            // Calculate first check digit
            int sum = 0;
            int weight = 5;
            
            for (int i = 0; i < 12; i++) {
                sum += Character.getNumericValue(cnpj.charAt(i)) * weight;
                weight = weight == 2 ? 9 : weight - 1;
            }
            
            int remainder = sum % 11;
            int firstCheckDigit = remainder < 2 ? 0 : 11 - remainder;
            
            if (firstCheckDigit != Character.getNumericValue(cnpj.charAt(12))) {
                return false;
            }
            
            // Calculate second check digit
            sum = 0;
            weight = 6;
            
            for (int i = 0; i < 13; i++) {
                sum += Character.getNumericValue(cnpj.charAt(i)) * weight;
                weight = weight == 2 ? 9 : weight - 1;
            }
            
            remainder = sum % 11;
            int secondCheckDigit = remainder < 2 ? 0 : 11 - remainder;
            
            return secondCheckDigit == Character.getNumericValue(cnpj.charAt(13));
            
        } catch (NumberFormatException e) {
            return false;
        }
    }
    
    @Override
    public String toString() {
        return value;
    }
}
