import re
import json
import ast

def extract_last_code_block(text: str) -> str:
    """
    Extract the last code block from text.
    
    Args:
        text (str): Input text containing code blocks
        
    Returns:
        str: The last code block content, or None if no code block found
    """
    code_blocks = re.findall(r'```.*?\n(.*?)```', text, re.DOTALL)
    if not code_blocks:
        code_blocks = re.findall(r'```(.*?)```', text, re.DOTALL)
    return code_blocks[-1].strip() if code_blocks else None

def parse_answer(answer: str, delimiter: str = None) -> list:
    """
    Parse answer string into a 2D list of integers.
    
    Args:
        answer (str): Answer string to parse
        delimiter (str, optional): Delimiter to split rows. Defaults to None.
        
    Returns:
        list: 2D list of integers, or None if parsing fails
    """
    if not answer:
        return None
    rows = answer.split("\n")
    result = []
    
    for row in rows:
        row = row.strip()
        if not row:
            continue
        
        columns = row.split(delimiter) if delimiter else row.split()
        result.append([int(grid) if grid.strip().isdigit() else None for grid in columns])
    
    return result if all(None not in row for row in result) else None

def parse_list_str(input_string: str) -> list:
    """
    Parse string representation of a list into actual list.
    
    Args:
        input_string (str): String representation of a list
        
    Returns:
        list: Parsed list, or None if parsing fails
    """
    try:
        return ast.literal_eval(input_string)
    except (ValueError, SyntaxError):
        return None

def preprocess_answer_to_matrix(raw_answer: str) -> list:
    """
    Preprocess raw answer into a matrix format.
    
    Args:
        raw_answer (str): Raw answer string
        
    Returns:
        list: Processed matrix, or None if processing fails
    """
    if isinstance(raw_answer, list):
        return raw_answer
    
    answer = parse_answer(raw_answer, delimiter=None)
    if answer is None:
        answer = parse_answer(raw_answer, delimiter=',')
    if answer is None:
        answer = parse_list_str(raw_answer)
    
    return answer

def is_valid_sudoku(board: list) -> bool:
    """
    Check if a Sudoku board is valid.
    
    Args:
        board (list): 9x9 Sudoku board
        
    Returns:
        bool: True if board is valid, False otherwise
    """
    def is_valid_row(board: list) -> bool:
        """Check if all rows are valid."""
        for row in board:
            if not is_valid_unit(row):
                return False
        return True

    def is_valid_column(board: list) -> bool:
        """Check if all columns are valid."""
        for col in zip(*board):  # Convert columns to rows
            if not is_valid_unit(col):
                return False
        return True

    def is_valid_subgrid(board: list) -> bool:
        """Check if all 3x3 subgrids are valid."""
        for i in range(0, 9, 3):
            for j in range(0, 9, 3):
                subgrid = [
                    board[x][y]
                    for x in range(i, i + 3)
                    for y in range(j, j + 3)
                ]
                if not is_valid_unit(subgrid):
                    return False
        return True

    def is_valid_unit(unit: list) -> bool:
        """Check if a unit (row, column or 3x3 subgrid) is valid."""
        length = len(unit)
        for i in range(1, length + 1):
            if i not in unit:
                return False
        return True

    return is_valid_row(board) and is_valid_column(board) and is_valid_subgrid(board)

def verify(pred: str, answer: str, meta: dict) -> int:
    """
    Verify if the predicted solution is correct.
    
    Args:
        pred (str): Predicted solution
        answer (str): Expected answer
        meta (dict): Metadata containing the puzzle
        
    Returns:
        int: 1 if solution is correct, 0 otherwise
    """
    if isinstance(answer, str):
        try:
            answer = json.loads(answer)
        except json.JSONDecodeError:
            pass
    
    if isinstance(meta, str):
        meta = json.loads(meta)
    elif not isinstance(meta, dict):
        raise ValueError('meta should be dict or str')

    puzzle = meta["question"]
    
    # Convert to 2D array
    if isinstance(puzzle, str):
        puzzle_array = []
        for line in puzzle.splitlines():
            # Skip empty lines
            if line.strip():
                # Replace . with 0, keep numbers as is
                row = [0 if char == '.' else int(char) for char in line.split()]
                puzzle_array.append(row)
        puzzle = puzzle_array
    
    pred = extract_last_code_block(pred)
    if pred is None:
        return 0
    normalized_pred = preprocess_answer_to_matrix(pred)
    if normalized_pred is None:
        return 0
    if len(normalized_pred) != 9:
        return 0
    try:
        for i in range(9):
            for j in range(9):
                if puzzle[i][j] != 0 and puzzle[i][j] != normalized_pred[i][j]:
                    return 0
        if is_valid_sudoku(normalized_pred):
            return 1
        else:
            return 0
    except Exception:
        return 0