from ..core.base import BaseValidator
import re

class CURP(BaseValidator):
    """
    Validator for Mexican CURP (Clave Única de Registro de Población).
    Format: 18 characters.
    """

    @classmethod
    def clean(cls, doc: str) -> str:
        return doc.upper().replace(' ', '').replace('-', '')

    @classmethod
    def validate(cls, doc: str) -> bool:
        doc = cls.clean(doc)
        
        if len(doc) != 18:
            return False
            
        # Regex for basic structure
        # 4 letters (Name)
        # 6 digits (Date YYMMDD)
        # 1 letter (Sex H/M)
        # 2 letters (State)
        # 3 letters (Consonants)
        # 1 alphanumeric (Homoclave)
        # 1 digit (Check digit)
        regex = r'^[A-Z]{4}[0-9]{6}[HM][A-Z]{2}[A-Z]{3}[A-Z0-9][0-9]$'
        
        if not re.match(regex, doc):
            return False
            
        # Check digit calculation
        # Characters dictionary
        chars = "0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
        # Wait, standard algorithm uses specific values.
        # "0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ" is not standard.
        # Standard is: 0-9, A-Z. Ñ is usually X.
        # Let's use a standard mapping.
        
        # Algorithm:
        # Sum of (Character Value * Position Weight)
        # Weights: 18, 17, 16, ..., 2
        # Check digit is 10 - (Sum % 10). If 10, then 0.
        
        # Mapping: 0-9 -> 0-9, A-Z -> 10-35.
        # Ñ is not allowed in CURP (replaced by X).
        
        mapping = {str(i): i for i in range(10)}
        for i, char in enumerate("ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
            mapping[char] = i + 10
            
        total = 0
        for i in range(17):
            char = doc[i]
            if char not in mapping:
                return False # Invalid char
            val = mapping[char]
            weight = 18 - i
            total += val * weight
            
        remainder = total % 10
        calculated_dv = 0 if remainder == 0 else 10 - remainder
        
        return int(doc[17]) == calculated_dv

    @classmethod
    def format(cls, doc: str) -> str:
        doc = cls.clean(doc)
        if not cls.validate(doc):
             raise ValueError("Invalid CURP format")
        return doc

    @classmethod
    def generate(cls) -> str:
        """Generates a valid CURP."""
        import random
        # Random valid components
        letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        name = "".join(random.choices(letters, k=4))
        date = f"{random.randint(50, 99)}{random.randint(1, 12):02d}{random.randint(1, 28):02d}" # 1950-1999
        sex = random.choice(['H', 'M'])
        state = random.choice(['AS', 'BC', 'BS', 'CC', 'CL', 'CM', 'CS', 'CH', 'DF', 'DG', 'GT', 'GR', 'HG', 'JC', 'MC', 'MN', 'MS', 'NT', 'NL', 'OC', 'PL', 'QT', 'QR', 'SP', 'SL', 'SR', 'TC', 'TS', 'TL', 'VZ', 'YN', 'ZS', 'NE'])
        consonants = "".join(random.choices(letters, k=3))
        homoclave = random.choice("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
        
        base = f"{name}{date}{sex}{state}{consonants}{homoclave}"
        
        # Calculate DV
        mapping = {str(i): i for i in range(10)}
        for i, char in enumerate("ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
            mapping[char] = i + 10
            
        total = 0
        for i in range(17):
            val = mapping[base[i]]
            weight = 18 - i
            total += val * weight
            
        remainder = total % 10
        dv = 0 if remainder == 0 else 10 - remainder
        
        return cls.format(base + str(dv))

    @classmethod
    def get_metadata(cls, doc: str) -> dict:
        """
        Extracts metadata from the CURP.
        Returns: {'birth_date': 'YYYY-MM-DD', 'gender': 'H/M', 'state': 'XX'}
        """
        doc = cls.clean(doc)
        if not cls.validate(doc):
            return None
        
        yy = doc[4:6]
        mm = doc[6:8]
        dd = doc[8:10]
        gender = doc[10]
        state = doc[11:13]
        century_indicator = doc[16]
        
        if century_indicator.isdigit():
            year = f"19{yy}"
        else:
            year = f"20{yy}"
            
        return {
            'birth_date': f"{year}-{mm}-{dd}",
            'gender': gender,
            'state': state
        }
