import re

def clean_ocr_text(text):
    """Remove special characters and extra spaces from OCR text."""
    if not text:
        return ""
    # Remove everything except Alphanumeric, spaces, and slash/dash for dates
    cleaned = re.sub(r'[^A-Z0-9\s\/\-]', '', text.upper())
    return " ".join(cleaned.split())

def clean_name(text):
    """Specialized cleaning for name fields: removes digits and noise words."""
    if not text:
        return ""
    # Remove digits and special characters (keeping only letters and spaces)
    cleaned = re.sub(r'[^A-Z\s]', '', text.upper())
    # Remove common OCR noise words often picked up from labels or stamps
    noise_words = [
        "HERES", "SIGNATURE", "NAME", "PHOTO", "FATHERS", "CARD", 
        "DATE", "NONINDIVIDUAL", "PERMANENT", "ACCOUNT", "DEPARTMENT"
    ]
    words = cleaned.split()
    filtered_words = [w for w in words if w not in noise_words]
    return " ".join(filtered_words)

def extract_pan_details_rule_based(lines):
    """
    Extract Name, Father's Name, and DOB using rule-based logic.
    Optimized for Indian PAN card layouts.
    """
    details = {
        "name": None,
        "father_name": None,
        "dob": None
    }
    
    # Normalize lines: Upper case, stripped, non-empty
    raw_lines = [l.strip().upper() for l in lines if l.strip()]
    
    # --- 1. DOB Extraction ---
    dob_pattern = r"\b(\d{2}/\d{2}/\d{4})\b"
    for line in raw_lines:
        match = re.search(dob_pattern, line)
        if match:
            details["dob"] = match.group(1)
            break

    # --- 2. Keyword Indexing ---
    name_keywords = ["NAME", "NON-INDIVIDUAL", "SIGNATURE", "ACCOUNT"]
    father_keywords = ["FATHER", "FATHER'S", "FATHER’S", "PITA", "PATI", "S/O"]
    header_keywords = ["INCOME", "TAX", "DEPARTMENT", "GOVT", "INDIA", "PERMANENT", "ACCOUNT", "CARD"]
    
    name_idx = -1
    father_idx = -1
    header_end_idx = 0
    
    for i, line in enumerate(raw_lines):
        # Find Father's Name marker
        if any(kw in line for kw in father_keywords):
            father_idx = i
            continue
        # Find Name marker (avoiding Father's Name line)
        if any(kw in line for kw in name_keywords) and not any(kw in line for kw in father_keywords):
            if name_idx == -1: # Pick the first Name marker
                name_idx = i
        # Track where the header usually ends (e.g., after 'Permanent Account Number')
        if any(kw in line for kw in ["PERMANENT", "ACCOUNT", "NUMBER"]):
            header_end_idx = i + 1

    # Heuristic: If name_idx is found but it's very high (garbage at top), reset it
    if name_idx != -1 and name_idx < header_end_idx - 2:
        name_idx = -1

    # --- 3. Targeted Extraction ---
    
    # Case A: Found both markers
    if name_idx != -1 and father_idx != -1:
        # Name is likely between name_idx and father_idx
        for i in range(name_idx + 1, father_idx):
            if len(raw_lines[i]) > 3:
                details["name"] = clean_name(raw_lines[i])
                break
        # Father's name is after father_idx
        for i in range(father_idx + 1, len(raw_lines)):
            if len(raw_lines[i]) > 3 and "DATE" not in raw_lines[i]:
                details["father_name"] = clean_name(raw_lines[i])
                break

    # Case B: Found only Father's Name marker
    elif father_idx != -1:
        # Name is likely the line IMMEDIATELY ABOVE the Father's Name marker line
        if father_idx > 0:
            potential_name = clean_name(raw_lines[father_idx - 1])
            if len(potential_name.split()) <= 4: # Names are usually short
                details["name"] = potential_name
            
        # Father's name is after the marker
        for i in range(father_idx + 1, len(raw_lines)):
            if len(raw_lines[i]) > 3 and "DATE" not in raw_lines[i]:
                details["father_name"] = clean_name(raw_lines[i])
                break

    # Case D: Found only Name marker (common in garbled OCR)
    elif name_idx != -1:
        # Name is likely the line below name_idx
        for i in range(name_idx + 1, min(name_idx + 3, len(raw_lines))):
            potential_name = clean_name(raw_lines[i])
            if potential_name and len(potential_name.split()) <= 4:
                details["name"] = potential_name
                
                # If we found name, maybe the line below it is Father's name even if label is missing
                if i + 1 < len(raw_lines):
                    potential_father = clean_name(raw_lines[i + 1])
                    if potential_father and len(potential_father.split()) <= 4 and "DATE" not in raw_lines[i+1]:
                        details["father_name"] = potential_father
                break

    # Case C: Fallback to heuristic (ignore headers)
    if not details["name"]:
        # Standard PAN: Starts after Income Tax/Govt of India/Permanent Account Number
        # Use header_end_idx as a starting point if available
        search_start = max(header_end_idx, 2) # Skip at least first 2 lines
        
        suitable_lines = []
        for l in raw_lines[search_start:]:
             # Ignore lines with common header/footer junk
             if len(l) > 4 and len(l) < 40 and not any(x in l for x in header_keywords + ["SIGNATURE", "DATE", "BIRTH"]):
                 suitable_lines.append(l)
        
        if len(suitable_lines) > 0:
            details["name"] = clean_name(suitable_lines[0])
        if not details["father_name"] and len(suitable_lines) > 1:
            details["father_name"] = clean_name(suitable_lines[1])

    return details
