import re
import json
import logging
from typing import List, Dict, Union, Optional, Any, TypedDict, Annotated
from pathlib import Path
import sys
import os

# Configure logger
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler('excel_edit_tools.log')
    ]
)
logger = logging.getLogger(__name__)

# Add the current directory to Python path
root_dir_path = Path(__file__).parent.parent.absolute()
sys.path.append(str(root_dir_path))
from writer.excel_writer import ExcelWriter


def write_formulas_to_excel_complex_agent(
    file_path: str,
    sheet_formulas: Dict[str, Dict[str, str]]
) -> List[Dict[str, Any]]:
    """
    Write formulas to specified cells across multiple sheets in an Excel workbook using ComplexAgentWriter.
    
    Args:
        workspace_path: Path to the Excel file
        sheet_formulas: Dictionary mapping sheet names to dictionaries of cell formulas
            Example: {
                "Sheet1": {"A1": "=SUM(B1:B10)", "B1": "=A1*2"},
                "Sheet2": {"C1": "=AVERAGE(A1:A10)"}
            }
        
    Returns:
        List of updated cell data dictionaries
    """
    logger.info(f"Starting to write formulas to Excel file using ExcelWriter: {file_path}")

    try:
        # Check if sheet_formulas is None or empty
        if not sheet_formulas:
            logger.warning("No sheet formulas provided")
            return []
        
        # Convert to the format expected by write_to_existing
        parsed_data = {}
        for sheet_name, cell_formulas in sheet_formulas.items():
            parsed_data[sheet_name] = []
            
            # Handle charts separately
            if 'charts' in cell_formulas:
                charts_data = cell_formulas['charts']
                # Store charts data in a special format that the writer can recognize
                for chart_name, chart_data in charts_data.items():
                    chart_entry = {"chart": chart_name}
                    chart_entry.update(chart_data)  # Add all chart properties
                    parsed_data[sheet_name].append(chart_entry)
                
                # Remove charts from cell_formulas to avoid processing them as cells
                cell_formulas = {k: v for k, v in cell_formulas.items() if k != 'charts'}
            
            # Handle data tables separately
            if 'data_tables' in cell_formulas:
                data_tables_data = cell_formulas['data_tables']
                # Store data tables data in a special format that the writer can recognize
                for data_table_name, data_table_data in data_tables_data.items():
                    data_table_entry = {"data_table": data_table_name}
                    data_table_entry.update(data_table_data)  # Add all data table properties
                    parsed_data[sheet_name].append(data_table_entry)
                
                # Remove data_tables from cell_formulas to avoid processing them as cells
                cell_formulas = {k: v for k, v in cell_formulas.items() if k != 'data_tables'}
            
            for cell, cell_data in cell_formulas.items():
                if isinstance(cell_data, dict):
                    # New format with formatting properties
                    cell_entry = {"cell": cell}
                    cell_entry.update(cell_data)  # Add all formatting properties
                else:
                    # Backward compatibility: simple formula string
                    cell_entry = {"cell": cell, "formula": cell_data}
                parsed_data[sheet_name].append(cell_entry)
        logger.debug(f"Converted {len(parsed_data)} sheets for Excel writing")
        
        # Use ComplexAgentWriter to write the data
        writer = ExcelWriter()
        logger.info("Starting to write data to Excel")
        
        success, updated_cells = writer.write_to_existing(
            data=parsed_data,
            output_filepath=file_path,
            create_pending=False,
            save=True
        )
        
        logger.info(f"Excel write operation {'succeeded' if success else 'failed'}")
        
        if success and updated_cells:
            return updated_cells
        else:
            logger.warning("No cells were updated or write operation failed")
            return []
            
    except Exception as e:
        logger.error(f"Error in write_formulas_to_excel: {str(e)}", exc_info=True)
        raise



def validate_cell_formats(formula_dict: Union[str, dict]) -> bool:
    """
    Validate if the input is a valid dictionary of sheet formulas.
    
    Expected format:
    {
        "Sheet1Name": {"A1": "=SUM(B1:B10)", "B1": "=A1*2"},
        "Sheet2Name": {"C1": "=AVERAGE(A1:A10)"}
    }
    
    Args:
        formula_dict: Either a JSON string or dict containing sheet formulas
    
    Returns:
        bool: True if valid, False otherwise
    """
    logger.debug("Starting cell format validation")
    
    # Parse JSON string if needed
    if isinstance(formula_dict, str):
        try:
            logger.debug("Parsing formula_dict from JSON string")
            formula_dict = json.loads(formula_dict)
        except json.JSONDecodeError as je:
            logger.error(f"Failed to parse JSON string: {str(je)}")
            return False
    
    # Handle case where the dictionary has string values that are JSON
    if isinstance(formula_dict, dict):
        for key, value in list(formula_dict.items()):
            if isinstance(value, str):
                try:
                    parsed_value = json.loads(value)
                    if isinstance(parsed_value, (dict, list)):
                        formula_dict[key] = parsed_value
                        logger.debug(f"Parsed nested JSON for key: {key}")
                except json.JSONDecodeError:
                    pass  # Not a JSON string, keep original value
    
    # Check if it's a dict
    if not isinstance(formula_dict, dict):
        logger.error(f"Expected dict, got {type(formula_dict).__name__}")
        return False
        
    # Handle the case where sheets are nested under a 'sheets' key
    if 'sheets' in formula_dict and isinstance(formula_dict['sheets'], dict):
        logger.debug("Found 'sheets' key, using its contents for validation")
        formula_dict = formula_dict['sheets']
    
    # Check each sheet's formulas
    for sheet_name, cell_formulas in formula_dict.items():
        logger.debug(f"Validating sheet: {sheet_name}")
        
        # Sheet name should be a non-empty string
        if not isinstance(sheet_name, str) or not sheet_name.strip():
            logger.error(f"Invalid sheet name: {sheet_name}")
            return False
            
        # Cell formulas should be a dict
        if not isinstance(cell_formulas, dict):
            logger.error(f"Expected dict for sheet {sheet_name}, got {type(cell_formulas).__name__}")
            return False
            
        # Check each cell formula in the sheet
        for cell_ref, cell_data in cell_formulas.items():
            # Skip charts section during validation
            if cell_ref == 'charts':
                continue
                
            # Ensure there's a valid cell reference
            if not cell_ref:
                logger.error("Empty cell reference is not allowed")
                return False

            #logger.debug(f"Validating cell {sheet_name}!{cell_ref}")
            
            # Check if cell reference format (single cell or range)
            if not re.match(r'^[A-Z]+[0-9]+(:[A-Z]+[0-9]+)?$', str(cell_ref)):
                logger.error(f"Invalid cell reference format: {cell_ref}")
                return False
            
            # cell_data can be either a string (formula) or dict (with formatting properties)
            # We don't require a formula to be present - formatting-only changes are valid
            
    
    logger.debug("Cell format validation successful")
    return True

def parse_markdown_formulas(markdown_input: str) -> Optional[Dict[str, Dict[str, str]]]:
    """
    Parse markdown-style cell formulas into a dictionary of sheet formulas.
    
    Expected input format:
        sheet_name: Sheet1| A1, "=SUM(B1:B10)" | B1, 100 | sheet_name: Sheet2| C1, "=A1*2"
    
    Args:
        markdown_input: String in markdown format with sheet names and cell formulas
        
    Returns:
        Dictionary with sheet names as keys and cell formulas as values if valid, None otherwise.
    """
    import re
    logger.info("Starting to parse markdown formulas")
    
    def clean_formula(formula: str) -> str:
        """Clean and unescape formula string while preserving internal quotes"""
        if not formula:
            return formula

        formula = formula.strip()
        # Always remove outer quotes for Excel formulas
        if (formula.startswith('"') and formula.endswith('"')) or \
           (formula.startswith("'") and formula.endswith("'")) or \
           (formula.startswith('[') and formula.endswith(']')):
            # Remove outer quotes and unescape
            inner = formula[1:-1]
            formula = inner.replace('\\"', '"').replace("\\'", "'")
        return formula
    
    def clean_number_format(number_format: str) -> str:
        """Clean and unescape number format string while preserving internal quotes"""
        if not number_format:
            return number_format

        number_format = number_format.strip()        
        # Always remove outer quotes for Excel number formats
        if (number_format.startswith('[') and number_format.endswith(']')) or \
           (number_format.startswith("'") and number_format.endswith("'")) or \
           (number_format.startswith('"') and number_format.endswith('"')):
            # Remove outer quotes and unescape
            number_format = number_format[1:-1]
        elif number_format.startswith('"') or number_format.startswith("'") or number_format.startswith('['):
            # Remove only leading quote if no matching trailing quote
            number_format = number_format[1:]
        elif number_format.endswith('"') or number_format.endswith("'") or number_format.endswith(']'):
            # Remove only trailing quote if no matching leading quote
            number_format = number_format[:-1]
        number_format = number_format.replace('\\"', '"').replace("\\'", "'")
        return number_format

    def is_valid_cell_reference(ref: str) -> bool:
        """Validate Excel cell reference format"""
        # Handles: A1, AA1, A$1, $A1, $A$1, Sheet1!A1, 'Sheet 1'!A1, A1:B10, A1:Z100
        pattern = r'^(\'.?|(.*\'!))?(\$?[A-Za-z]+\$?[0-9]+(:\$?[A-Za-z]+\$?[0-9]+)?)$'
        return bool(re.match(pattern, ref))

    def parse_border_properties(border_string):
        """
        Parse border string properties (l, c, w) into a dictionary.
        
        Args:
            border_string (str): Border string in format "l=2c=#000000w=4" or "l=2, c=#000000, w=4"
        
        Returns:
            dict: Dictionary with keys 'line_style', 'color', 'weight' and their respective values
        
        Examples:
            >>> parse_border_properties("l=2c=#000000w=4")
            {'line_style': 2, 'color': '#000000', 'weight': 4}
            
            >>> parse_border_properties("l=1, c=#FF0000, w=2")
            {'line_style': 1, 'color': '#FF0000', 'weight': 2}
        """
        # Initialize result dictionary
        result = {
            'line_style': None,
            'color': None,
            'weight': None
        }
        
        # Remove any spaces and quotes from the string
        border_string = border_string.replace(' ', '').replace('"', '').replace("'", '')
        
        # Pattern to match l=value
        line_style_match = re.search(r'l=(-?\d+)', border_string)
        if line_style_match:
            result['line_style'] = int(line_style_match.group(1))
        
        # Pattern to match c=#hexcode
        color_match = re.search(r'c=(#[A-Fa-f0-9]{6})', border_string)
        if color_match:
            result['color'] = color_match.group(1)
        
        # Pattern to match w=value
        weight_match = re.search(r'w=(\d+)', border_string)
        if weight_match:
            result['weight'] = int(weight_match.group(1))
        
        return result
    
    try:
        if not markdown_input or not isinstance(markdown_input, str):
            logger.error("Invalid markdown input: empty or not a string")
            return None
            
        result = {}
        current_sheet = None
        
        # Split by 'sheet_name:' to separate different sheets
        sheet_sections = [s.strip() for s in markdown_input.split('sheet_name:') if s.strip()]
        
        for section in sheet_sections:
            if not section:
                continue
                
            # Split into sheet name, cell, and chart entries
            parts = [p.strip() for p in section.split('|', 1)]
            if not parts:
                continue
                
            sheet_name = parts[0].strip()
            if not sheet_name:
                logger.warning("Empty sheet name found, skipping section")
                continue
                
            current_sheet = sheet_name
            result[current_sheet] = {}
            
            if len(parts) == 1:  # No cell entries for this sheet
                continue
                
            # Process cell entries
            cell_entries = [e.strip() for e in parts[1].split('|') if e.strip()]


            for entry in cell_entries:
                if not entry:
                    continue

                # First, extract data validation brackets separately to avoid CSV splitting issues
                import re
                
                # Extract dv=[...] content first
                dv_validation_content = None
                def extract_dv_brackets(match):
                    nonlocal dv_validation_content
                    dv_validation_content = match.group(1)  # Save the content
                    return "dv=DV_PLACEHOLDER"
                
                # Extract dv=[...] brackets separately
                entry_dv_extracted = re.sub(r'dv=\[(.*?)\]', extract_dv_brackets, entry)
                
                # Extract num_fmt=[...] brackets separately
                num_fmt_content = None
                def extract_num_fmt_brackets(match):
                    nonlocal num_fmt_content
                    num_fmt_content = match.group(1)  # Save the content
                    return "num_fmt=NUM_FMT_PLACEHOLDER"
                entry_num_fmt_extracted = re.sub(r'num_fmt=\[(.*?)\]', extract_num_fmt_brackets, entry_dv_extracted)
                
                # Extract cmt=[...] brackets separately
                comment_content = None
                def extract_comment_brackets(match):
                    nonlocal comment_content
                    comment_content = match.group(1)  # Save the content
                    return "cmt=COMMENT_PLACEHOLDER"
                entry_comment_extracted = re.sub(r'cmt=\[(.*?)\]', extract_comment_brackets, entry_num_fmt_extracted)
                
                # Extract cf=[...] brackets separately for conditional formatting
                cf_content = None
                def extract_cf_brackets(match):
                    nonlocal cf_content
                    cf_content = match.group(1)  # Save the content
                    return "cf=CF_PLACEHOLDER"
                entry_cf_extracted = re.sub(r'cf=\[(.*?)\]', extract_cf_brackets, entry_comment_extracted)
                
                # Extract ALL chart font properties that use brackets
                chart_font_properties = {}
                def extract_chart_font_brackets(match):
                    font_key = match.group(1)  # e.g., 'title_font', 'x_title_font', 'y_title_font'
                    font_value = match.group(2)  # The font specification
                    chart_font_properties[font_key] = font_value
                    return f"{font_key}=FONT_PLACEHOLDER"
                
                # Extract all possible chart font properties
                entry_chart_fonts_extracted = re.sub(r'(title_font|x_title_font|y_title_font)=\[(.*?)\]', extract_chart_font_brackets, entry_comment_extracted)
                
                # Extract dta_tbl=[...] brackets separately with proper nesting handling
                data_table_content = None
                def extract_data_table_brackets(match):
                    nonlocal data_table_content
                    data_table_content = match.group(1)  # Save the content
                    return "dta_tbl=DATA_TABLE_PLACEHOLDER"
                
                # Use regex that handles nested brackets for data tables
                entry_data_table_extracted = re.sub(r'dta_tbl=\[([^\[\]]*(?:\[[^\[\]]*\][^\[\]]*)*)\]', extract_data_table_brackets, entry_chart_fonts_extracted)
                
                # Now extract formula brackets (any remaining brackets should be formula)
                formula_content = None
                def extract_formula_brackets(match):
                    nonlocal formula_content
                    formula_content = match.group(1)  # Save the content
                    return "FORMULA_PLACEHOLDER"  
                entry_without_brackets = re.sub(r'\[(.*?)\]', extract_formula_brackets, entry_data_table_extracted)
    
                    
                # Split into cell reference, formula/value, and formatting properties
                # Use CSV-aware parsing to handle commas inside quoted strings
                import csv
                import io
                
                try:
                    # Pre-process entry to remove spaces around commas for proper CSV parsing
                    # But preserve spaces inside quoted strings
                    processed_entry = entry_without_brackets
                    
                    # Simple approach: replace ', ' with ',' outside of quotes
                    import re
                    # This regex matches comma followed by space, but not inside quotes
                    processed_entry = re.sub(r',\s+(?=(?:[^"]*"[^"]*")*[^"]*$)', ',', processed_entry)
                    
                    # Use CSV reader to properly handle quoted strings with commas
                    csv_reader = csv.reader(io.StringIO(processed_entry), delimiter=',')
                    cell_parts = [p.strip() for p in next(csv_reader)]
                except (csv.Error, StopIteration):
                    # Fall back to simple split if CSV parsing fails
                    cell_parts = [p.strip() for p in entry.split(',')]
                if len(cell_parts) < 2:
                    logger.warning(f"Invalid cell entry format: {entry}")
                    continue
                    
                cell_ref = cell_parts[0].strip()                
                if not cell_ref:
                    logger.warning("Empty cell reference found, skipping")
                    continue
                
                # Check if this is a chart entry by looking for chart_name= in any of the parts
                chart_name = None
                chart_data = {}
                is_chart_entry = False
                
                # Check if any part contains chart_name=
                for part in cell_parts:
                    if 'chart_name=' in part:
                        # Extract the chart name
                        key, value = part.split('=', 1)
                        if key.strip() == 'chart_name':
                            chart_name = value.strip().strip('"\'')
                            is_chart_entry = True
                            break
                
                # If no chart_name= found, check if cell_ref is not a valid cell reference (fallback)
                if not is_chart_entry and not is_valid_cell_reference(cell_ref):
                    # This might be a chart entry using old format
                    chart_name = cell_ref
                    is_chart_entry = True
                
                if is_chart_entry:
                    # Process chart properties
                    for i in range(1, len(cell_parts)):
                        prop_part = cell_parts[i].strip()
                        if '=' in prop_part:
                            key, value = prop_part.split('=', 1)
                            key = key.strip()
                            value = value.strip().strip('"\'')
                            
                            # Skip the chart_name property as we already processed it
                            if key == 'chart_name':
                                continue
                            
                            # Parse chart properties
                            if key == 'type':
                                chart_data['type'] = value
                            elif key == 'height':
                                chart_data['height'] = value
                            elif key == 'width':
                                chart_data['width'] = value
                            elif key == 'left':
                                chart_data['left'] = value
                            elif key == 'top':
                                chart_data['top'] = value
                            elif key == 'x' or key == 'x_axis':
                                chart_data['x_axis'] = value
                            elif key.startswith('s') and key[1:].isdigit():
                                # Series data (s1, s2, s3, etc.)
                                series_num = key[1:] if len(key) > 1 else '1'
                                chart_data[f'series_{series_num}'] = value
                            elif key.startswith('series_') and not key.endswith('_name'):
                                # Series data (series_1, series_2, series_3, etc.)
                                chart_data[key] = value
                            elif key.startswith('series_') and key.endswith('_name'):
                                # Series names (series_1_name, series_2_name, etc.)
                                chart_data[key] = value
                            elif key == 'delete':
                                chart_data['delete'] = value.lower() == 'true'
                            # Title properties
                            elif key == 'title':
                                chart_data['title'] = value
                            elif key == 'title_pos':
                                chart_data['title_pos'] = value
                            elif key == 'title_font':
                                # Use extracted font content from chart_font_properties
                                if 'title_font' in chart_font_properties:
                                    font_value = chart_font_properties['title_font']
                                    font_parts = font_value.split(',')
                                    if len(font_parts) >= 5:
                                        chart_data['title_font'] = {
                                            'name': font_parts[0].strip(),
                                            'size': int(font_parts[1].strip()),
                                            'bold': font_parts[2].strip().lower() == 'true',
                                            'italic': font_parts[3].strip().lower() == 'true',
                                            'color': font_parts[4].strip()
                                        }
                                    else:
                                        chart_data['title_font'] = font_value
                                else:
                                    # Fallback for backward compatibility
                                    chart_data['title_font'] = value
                            # Legend properties
                            elif key == 'legend':
                                chart_data['legend'] = value.lower() == 'true'
                            elif key == 'legend_pos':
                                chart_data['legend_pos'] = value
                            # X-Axis properties
                            elif key == 'x_title':
                                chart_data['x_title'] = value
                            elif key == 'x_title_font':
                                # Parse font format: [Arial,10,false,false,#000000]
                                if value.startswith('[') and value.endswith(']'):
                                    font_parts = value[1:-1].split(',')
                                    if len(font_parts) >= 5:
                                        chart_data['x_title_font'] = {
                                            'name': font_parts[0].strip(),
                                            'size': int(font_parts[1].strip()),
                                            'bold': font_parts[2].strip().lower() == 'true',
                                            'italic': font_parts[3].strip().lower() == 'true',
                                            'color': font_parts[4].strip()
                                        }
                                else:
                                    chart_data['x_title_font'] = value
                            elif key == 'x_labels':
                                chart_data['x_labels'] = value.lower() == 'true'
                            elif key == 'x_ticks':
                                chart_data['x_ticks'] = value.lower() == 'true'
                            elif key == 'x_grid':
                                chart_data['x_grid'] = value.lower() == 'true'
                            elif key == 'x_grid_color':
                                chart_data['x_grid_color'] = value
                            elif key == 'x_line_color':
                                chart_data['x_line_color'] = value
                            elif key == 'x_line_weight':
                                chart_data['x_line_weight'] = int(value)
                            # Y-Axis properties
                            elif key == 'y_title':
                                chart_data['y_title'] = value
                            elif key == 'y_title_font':
                                # Parse font format: [Arial,10,false,false,#000000]
                                if value.startswith('[') and value.endswith(']'):
                                    font_parts = value[1:-1].split(',')
                                    if len(font_parts) >= 5:
                                        chart_data['y_title_font'] = {
                                            'name': font_parts[0].strip(),
                                            'size': int(font_parts[1].strip()),
                                            'bold': font_parts[2].strip().lower() == 'true',
                                            'italic': font_parts[3].strip().lower() == 'true',
                                            'color': font_parts[4].strip()
                                        }
                                else:
                                    chart_data['y_title_font'] = value
                            elif key == 'y_labels':
                                chart_data['y_labels'] = value.lower() == 'true'
                            elif key == 'y_ticks':
                                chart_data['y_ticks'] = value.lower() == 'true'
                            elif key == 'y_grid':
                                chart_data['y_grid'] = value.lower() == 'true'
                            elif key == 'y_grid_color':
                                chart_data['y_grid_color'] = value
                            elif key == 'y_line_color':
                                chart_data['y_line_color'] = value
                            elif key == 'y_line_weight':
                                chart_data['y_line_weight'] = int(value)
                            # Series colors (s1_color, s2_color, etc.)
                            elif key.startswith('s') and key.endswith('_color') and len(key) > 3:
                                # Extract series number from s1_color, s2_color, etc.
                                series_num = key[1:-6]  # Remove 's' prefix and '_color' suffix
                                if series_num.isdigit():
                                    chart_data[f's{series_num}_color'] = value
                            # Plot area properties
                            elif key == 'plot_fill':
                                chart_data['plot_fill'] = value
                            elif key == 'plot_border':
                                chart_data['plot_border'] = value.lower() == 'true'
                            elif key == 'plot_border_color':
                                chart_data['plot_border_color'] = value
                            # Chart area properties
                            elif key == 'chart_fill':
                                chart_data['chart_fill'] = value
                            elif key == 'chart_border':
                                chart_data['chart_border'] = value.lower() == 'true'
                            elif key == 'chart_border_color':
                                chart_data['chart_border_color'] = value
                            else:
                                # Store any other chart properties
                                chart_data[key] = value
                    
                    # Initialize charts section if it doesn't exist
                    if 'charts' not in result[current_sheet]:
                        result[current_sheet]['charts'] = {}
                    
                    result[current_sheet]['charts'][chart_name] = chart_data
                    continue
                
                # Process extracted content
                cell_data = {}
                
                # Handle formula content
                if formula_content:
                    formula = clean_formula(formula_content)
                    if formula and not formula.lower() == 'no_change': #no_change is shorthand when we don't want to update a cell formula
                        cell_data['formula'] = formula
                
                # Handle number format content
                if num_fmt_content:
                    cleaned_num_fmt = clean_number_format(num_fmt_content)
                    if cleaned_num_fmt:
                        cell_data['number_format'] = cleaned_num_fmt
                        logger.debug(f"Set number format for {cell_ref}: {cleaned_num_fmt}")
                
                # Handle comment content
                if comment_content:
                    cleaned_comment = clean_formula(comment_content)
                    if cleaned_comment:
                        cell_data['comment'] = cleaned_comment
                
                # Handle conditional formatting content
                if cf_content:
                    cf_data = parse_conditional_formatting(cf_content)
                    if cf_data:
                        cell_data['conditional_formatting'] = cf_data
                
                # Process formatting properties from remaining parts
                for i in range(2, len(cell_parts)):
                    prop_part = cell_parts[i].strip()
                    if '=' in prop_part:
                        key, value = prop_part.split('=', 1)
                        key = key.strip()
                        value = value.strip()
                        
                        # Parse different property types
                        if key == 'b':  # bold
                            cell_data['bold'] = value.lower() == 'true'
                        elif key == 'it':  # italic
                            cell_data['italic'] = value.lower() == 'true'
                        elif key == 'u':  # underline
                            cell_data['underline'] = value.lower() == 'true'
                        elif key == 's':  # strikethrough
                            cell_data['strikethrough'] = value.lower() == 'true'
                        elif key == 'sz':  # font size
                            try:
                                cell_data['font_size'] = float(clean_formula(value))
                            except (ValueError, TypeError):
                                logger.warning(f"Invalid font size value: {value}")
                        elif key == 'st':  # font style
                            cell_data['font_style'] = clean_formula(value)
                        elif key == 'font':  # font color
                            cell_data['text_color'] = clean_formula(value)
                        elif key == 'fill':  # fill color
                            cell_data['fill_color'] = clean_formula(value)
                        elif key == 'wrap':  # wrap text
                            cell_data['wrap_text'] = value.lower() == 'true'
                        elif key == 'ha':  # horizontal alignment
                            cell_data['horizontal_alignment'] = value
                        elif key == 'va':  # vertical alignment
                            cell_data['vertical_alignment'] = value
                        elif key == 'ind':  # indent
                            cell_data['indent'] = value
                        elif key == 'bord_t':  
                            cell_data['border_top'] = parse_border_properties(value)
                        elif key == 'bord_b':  
                            cell_data['border_bottom'] = parse_border_properties(value)
                        elif key == 'bord_l': 
                            cell_data['border_left'] = parse_border_properties(value)
                        elif key == 'bord_r':  
                            cell_data['border_right'] = parse_border_properties(value)
                        elif key == 'dv':  # data validation
                            # Use the extracted validation content from dv=[...] brackets
                            if dv_validation_content:
                                cell_data['data_validation'] = clean_formula(dv_validation_content)
                            else:
                                # Fallback: handle bracketed validation format manually
                                validation_text = value
                                if validation_text.startswith('[') and validation_text.endswith(']'):
                                    validation_text = validation_text[1:-1]  # Remove brackets
                                    cell_data['data_validation'] = clean_formula(validation_text)
                                else:
                                    # If no brackets, use the value as is
                                    cell_data['data_validation'] = clean_formula(validation_text)
                    
                result[current_sheet][cell_ref.upper()] = cell_data
            
            # Process data table if extracted
            if data_table_content:
                try:
                    parsed_data_table = parse_data_table_properties(data_table_content)
                    
                    # Validate and infer header cells
                    if validate_and_infer_data_table_cells(parsed_data_table, current_sheet, result):
                        # Initialize data_tables section if it doesn't exist
                        if 'data_tables' not in result[current_sheet]:
                            result[current_sheet]['data_tables'] = {}
                        
                        # Store data table with a unique identifier
                        table_id = f"table_{len(result[current_sheet]['data_tables']) + 1}"
                        result[current_sheet]['data_tables'][table_id] = parsed_data_table
                        
                        logger.info(f"Data table {table_id} parsed and validated successfully")
                    else:
                        logger.warning("Data table validation failed, skipping")
                        
                except Exception as e:
                    logger.error(f"Error processing data table: {e}")
        
        if not result:
            logger.error("No valid sheets or cell entries found in markdown")
            return None
            
        logger.info(f"Successfully parsed {sum(len(s) for s in result.values())} formulas "
                  f"across {len(result)} sheets from markdown")
        return result
        
    except Exception as e:
        logger.error(f"Error in parse_markdown_formulas: {str(e)}", exc_info=True)
        return None

def parse_data_table_properties(data_table_string: str) -> Dict[str, Any]:
    """Parse data table properties handling nested brackets"""
    properties = {}
    
    # Split by commas, but handle nested brackets properly
    parts = []
    current_part = ""
    bracket_level = 0
    
    for char in data_table_string:
        if char == '[':
            bracket_level += 1
        elif char == ']':
            bracket_level -= 1
        elif char == ',' and bracket_level == 0:
            parts.append(current_part.strip())
            current_part = ""
            continue
        current_part += char
    
    if current_part:
        parts.append(current_part.strip())
    
    # Parse each part
    for part in parts:
        if '=' in part:
            key, value = part.split('=', 1)
            key = key.strip()
            value = value.strip()
            
            if key in ['row_vals', 'col_vals'] and value.startswith('[') and value.endswith(']'):
                # Parse array values with validation
                array_content = value[1:-1]
                try:
                    values = []
                    for item in array_content.split(','):
                        item = item.strip()
                        if item:
                            # Try to parse as number, fallback to string
                            try:
                                values.append(float(item))
                            except ValueError:
                                values.append(item)  # Keep as string for text headers
                    properties[key] = values
                except Exception as e:
                    logger.warning(f"Error parsing {key} values: {e}")
                    properties[key] = []
            else:
                properties[key] = value
    
    return properties

def validate_and_infer_data_table_cells(data_table_data: Dict[str, Any], current_sheet: str, result: Dict) -> bool:
    """Validate data table parameters and infer header cell positions"""
    try:
        # Extract required parameters
        input_cell = data_table_data.get('i')
        row_input = data_table_data.get('r')
        col_input = data_table_data.get('c')
        range_str = data_table_data.get('rng')
        row_vals = data_table_data.get('row_vals', [])
        col_vals = data_table_data.get('col_vals', [])
        
        # Validate required parameters
        if not all([input_cell, row_input, col_input, range_str]):
            logger.error("Missing required data table parameters")
            return False
        
        # Parse range to determine dimensions
        range_parts = range_str.split(':')
        if len(range_parts) != 2:
            logger.error(f"Invalid range format: {range_str}")
            return False
            
        start_cell = range_parts[0]
        end_cell = range_parts[1]
        
        # Extract row and column info
        start_row = int(re.search(r'\d+', start_cell).group())
        start_col = re.search(r'[A-Z]+', start_cell).group()
        end_row = int(re.search(r'\d+', end_cell).group())
        end_col = re.search(r'[A-Z]+', end_cell).group()
        
        # Calculate expected dimensions
        expected_rows = end_row - start_row + 1
        expected_cols = ord(end_col) - ord(start_col) + 1
        
        # Validate row_vals count
        if len(row_vals) != expected_rows:
            logger.error(f"Row values count ({len(row_vals)}) doesn't match range rows ({expected_rows})")
            return False
        
        # Validate col_vals count
        if len(col_vals) != expected_cols:
            logger.error(f"Column values count ({len(col_vals)}) doesn't match range columns ({expected_cols})")
            return False
        
        # Infer and populate row header cells (to the left of range)
        left_col = chr(ord(start_col) - 1)
        for i, val in enumerate(row_vals):
            cell_ref = f"{left_col}{start_row + i}"
            if cell_ref.upper() not in result[current_sheet]:
                result[current_sheet][cell_ref.upper()] = {'formula': str(val)}
                logger.debug(f"Inferred row header cell {cell_ref}: {val}")
        
        # Infer and populate column header cells (above the range)
        header_row = start_row - 1
        for i, val in enumerate(col_vals):
            col_letter = chr(ord(start_col) + i)
            cell_ref = f"{col_letter}{header_row}"
            if cell_ref.upper() not in result[current_sheet]:
                result[current_sheet][cell_ref.upper()] = {'formula': str(val)}
                logger.debug(f"Inferred column header cell {cell_ref}: {val}")
        
        # Store validated data table info
        data_table_data['validated'] = True
        data_table_data['expected_rows'] = expected_rows
        data_table_data['expected_cols'] = expected_cols
        data_table_data['row_header_cells'] = [f"{left_col}{start_row + i}" for i in range(expected_rows)]
        data_table_data['col_header_cells'] = [f"{chr(ord(start_col) + i)}{header_row}" for i in range(expected_cols)]
        
        logger.info(f"Data table validation successful for range {range_str}")
        return True
        
    except Exception as e:
        logger.error(f"Error validating data table: {e}")
        return False

def parse_cell_formulas(formula_input: Union[str, dict]) -> Optional[Dict[str, Dict[str, str]]]:
    """
    Parse and validate cell formulas into a dictionary of sheet formulas.
    
    Args:
        formula_input: Either a JSON string or dict in format:
            {
                "Sheet1Name": {"A1": "=SUM(B1:B10)", "B1": "=A1*2"},
                "Sheet2Name": {"C1": "=AVERAGE(A1:A10)"}
            }
            
    Returns:
        Dictionary with sheet names as keys and cell formulas as values if valid, None otherwise
    """
    logger.info("Starting to parse cell formulas")
    
    try:
        # Log input type for debugging
        input_type = type(formula_input).__name__
        logger.debug(f"Input type: {input_type}, content sample: {str(formula_input)[:200]}...")
        
        # If input is a string, parse it as JSON
        if isinstance(formula_input, str):
            try:
                formula_dict = json.loads(formula_input)
                logger.debug("Successfully parsed input string as JSON")
            except json.JSONDecodeError as je:
                logger.error(f"Failed to parse input as JSON: {str(je)}")
                return None
        else:
            formula_dict = formula_input
            logger.debug("Using input as dictionary directly")
        
        # Clean formulas that might have extra quotes from JSON parsing
        def clean_formula_from_json(formula: str) -> str:
            """Clean formulas that might have extra quotes from JSON parsing"""
            if not formula:
                return formula
            formula = formula.strip()
            # Remove outer quotes if they exist (both matching or just leading)
            # Remove outer square brackets if they exist (both matching or just leading)
            if (formula.startswith('[') and formula.endswith(']')) or \
               (formula.startswith('"') and formula.endswith('"')) or \
               (formula.startswith("'") and formula.endswith("'")):
                formula = formula[1:-1]
            elif formula.startswith('"') or formula.startswith("'") or formula.startswith('['):
                # Remove only leading quote if no matching trailing quote
                formula = formula[1:]
            formula = formula.strip()
            formula = formula.replace('\\"', '"').replace("\\'", "'")
            return formula
        
        # Clean all formulas in the dictionary
        for sheet_name, cell_formulas in formula_dict.items():
            if isinstance(cell_formulas, dict):
                for cell_ref, formula in cell_formulas.items():
                    if isinstance(formula, str):
                        formula_dict[sheet_name][cell_ref] = clean_formula_from_json(formula)
                    elif isinstance(formula, dict) and 'formula' in formula:
                        formula_dict[sheet_name][cell_ref]['formula'] = clean_formula_from_json(formula['formula'])
        
        # Validate the cell formats
        logger.debug("Validating cell formats")
        if not validate_cell_formats(formula_dict):
            logger.error("Cell format validation failed")
            return None
            
        logger.info(f"Successfully parsed {sum(len(s) for s in formula_dict.values())} formulas across {len(formula_dict)} sheets")
        return formula_dict
        
    except Exception as e:
        logger.error(f"Error in parse_cell_formulas: {str(e)}", exc_info=True)
        return None


def parse_conditional_formatting(cf_content: str) -> Dict[str, Any]:
    """
    Parse conditional formatting content into a structured format.
    
    Args:
        cf_content: Conditional formatting string like "type:cell_value,operator:greater,value1:100,background_color:#FF0000"
        
    Returns:
        Dictionary with conditional formatting parameters
    """
    cf_data = {}
    
    try:
        # Split by comma and parse key:value pairs
        parts = cf_content.split(',')
        for part in parts:
            if ':' in part:
                key, value = part.split(':', 1)
                key = key.strip()
                value = value.strip()
                
                # Type conversions
                if key in ['value1', 'value2']:
                    try:
                        value = float(value)
                    except ValueError:
                        pass  # Keep as string
                elif key in ['type', 'operator', 'background_color', 'font_color']:
                    pass  # Keep as string
                
                cf_data[key] = value
        
        logger.debug(f"Parsed conditional formatting: {cf_data}")
        return cf_data
        
    except Exception as e:
        logger.error(f"Error parsing conditional formatting: {e}")
        return {}
