"""
Pawn Structure Analysis Tool
Evaluates pawn formations, weaknesses, and strengths.

Returns structured analysis of:
- Pawn islands (groups of connected pawns)
- Passed pawns (no enemy pawns blocking advancement)
- Doubled pawns (multiple pawns on same file)
- Isolated pawns (no friendly pawns on adjacent files)
- Backward pawns (can't advance safely, no support)
- Pawn chains and their strength
"""

import chess
from typing import Dict, List, Set, Tuple


def analyze_pawn_structure(fen: str) -> Dict:
    """
    Analyze pawn structure for a position.
    
    Args:
        fen: Position in FEN notation
    
    Returns:
        Dictionary with:
            - score: Pawn structure quality differential (positive = white better)
            - factors: List of contributing factors (weaknesses, strengths)
            - evidence: Human-readable explanations
            - details: Detailed breakdown by side
    
    Example:
        >>> result = analyze_pawn_structure("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
        >>> result['evidence']
        ['Both sides have healthy pawn structures', ...]
    """
    board = chess.Board(fen)
    
    # Analyze both sides
    white_structure = _analyze_side_pawns(board, chess.WHITE)
    black_structure = _analyze_side_pawns(board, chess.BLACK)
    
    # Calculate structure quality differential
    score = white_structure['score'] - black_structure['score']
    
    factors = []
    evidence = []
    
    # Compare pawn islands
    white_islands = white_structure['pawn_islands']
    black_islands = black_structure['pawn_islands']
    
    if white_islands < black_islands:
        factors.append({
            'type': 'fewer_pawn_islands',
            'side': 'white',
            'count': white_islands
        })
        evidence.append(f"White has fewer pawn islands ({white_islands} vs {black_islands}) - more compact structure")
    elif black_islands < white_islands:
        factors.append({
            'type': 'fewer_pawn_islands',
            'side': 'black',
            'count': black_islands
        })
        evidence.append(f"Black has fewer pawn islands ({black_islands} vs {white_islands}) - more compact structure")
    
    # Passed pawns
    white_passed = white_structure['passed_pawns']
    black_passed = black_structure['passed_pawns']
    
    if white_passed:
        for square in white_passed:
            rank = chess.square_rank(square)
            file_name = chess.square_name(square)[0]
            factors.append({
                'type': 'passed_pawn',
                'side': 'white',
                'square': chess.square_name(square),
                'advancement': rank  # Higher rank = more advanced
            })
            evidence.append(f"White passed pawn on {file_name}{rank + 1}")
    
    if black_passed:
        for square in black_passed:
            rank = chess.square_rank(square)
            file_name = chess.square_name(square)[0]
            factors.append({
                'type': 'passed_pawn',
                'side': 'black',
                'square': chess.square_name(square),
                'advancement': 7 - rank  # Lower rank for black = more advanced
            })
            evidence.append(f"Black passed pawn on {file_name}{rank + 1}")
    
    # Doubled pawns
    white_doubled = white_structure['doubled_pawns']
    black_doubled = black_structure['doubled_pawns']
    
    if white_doubled > 0:
        factors.append({
            'type': 'doubled_pawns',
            'side': 'white',
            'count': white_doubled
        })
        evidence.append(f"White has {white_doubled} doubled pawn(s) - weakness")
    
    if black_doubled > 0:
        factors.append({
            'type': 'doubled_pawns',
            'side': 'black',
            'count': black_doubled
        })
        evidence.append(f"Black has {black_doubled} doubled pawn(s) - weakness")
    
    # Isolated pawns
    white_isolated = white_structure['isolated_pawns']
    black_isolated = black_structure['isolated_pawns']
    
    if white_isolated:
        for square in white_isolated:
            file_name = chess.square_name(square)[0]
            factors.append({
                'type': 'isolated_pawn',
                'side': 'white',
                'square': chess.square_name(square)
            })
            evidence.append(f"White isolated pawn on {file_name}-file")
    
    if black_isolated:
        for square in black_isolated:
            file_name = chess.square_name(square)[0]
            factors.append({
                'type': 'isolated_pawn',
                'side': 'black',
                'square': chess.square_name(square)
            })
            evidence.append(f"Black isolated pawn on {file_name}-file")
    
    # Backward pawns
    white_backward = white_structure['backward_pawns']
    black_backward = black_structure['backward_pawns']
    
    if white_backward:
        for square in white_backward:
            file_name = chess.square_name(square)[0]
            factors.append({
                'type': 'backward_pawn',
                'side': 'white',
                'square': chess.square_name(square)
            })
            evidence.append(f"White backward pawn on {file_name}-file")
    
    if black_backward:
        for square in black_backward:
            file_name = chess.square_name(square)[0]
            factors.append({
                'type': 'backward_pawn',
                'side': 'black',
                'square': chess.square_name(square)
            })
            evidence.append(f"Black backward pawn on {file_name}-file")
    
    # Pawn majorities
    _analyze_pawn_majorities(white_structure, black_structure, evidence, factors)
    
    # If no significant features, mention healthy structure
    if not factors:
        evidence.append("Both sides have healthy pawn structures")
    
    return {
        'score': score,
        'factors': factors,
        'evidence': evidence,
        'details': {
            'white': white_structure,
            'black': black_structure
        }
    }


def _analyze_side_pawns(board: chess.Board, color: chess.Color) -> Dict:
    """
    Analyze pawn structure for one side.
    
    Args:
        board: Chess board
        color: Color to analyze
    
    Returns:
        Dictionary with pawn structure metrics
    """
    pawns = board.pieces(chess.PAWN, color)
    pawn_squares = list(pawns)
    
    # Get pawns by file
    pawns_by_file = _get_pawns_by_file(pawn_squares)
    
    # Count pawn islands
    pawn_islands = _count_pawn_islands(pawns_by_file)
    
    # Find passed pawns
    passed_pawns = _find_passed_pawns(board, color, pawn_squares)
    
    # Count doubled pawns
    doubled_pawns = _count_doubled_pawns(pawns_by_file)
    
    # Find isolated pawns
    isolated_pawns = _find_isolated_pawns(pawns_by_file, pawn_squares)
    
    # Find backward pawns
    backward_pawns = _find_backward_pawns(board, color, pawn_squares, pawns_by_file)
    
    # Calculate pawn structure score
    # Pawn islands: -10 per island beyond 1
    # Passed pawns: +40 per passed pawn, +10 per rank advanced
    # Doubled pawns: -15 per doubled pawn
    # Isolated pawns: -20 per isolated pawn
    # Backward pawns: -15 per backward pawn
    
    score = 0
    score -= (pawn_islands - 1) * 10
    
    for square in passed_pawns:
        rank = chess.square_rank(square)
        advancement = rank if color == chess.WHITE else (7 - rank)
        score += 40 + (advancement * 10)
    
    score -= doubled_pawns * 15
    score -= len(isolated_pawns) * 20
    score -= len(backward_pawns) * 15
    
    return {
        'pawn_islands': pawn_islands,
        'passed_pawns': passed_pawns,
        'doubled_pawns': doubled_pawns,
        'isolated_pawns': isolated_pawns,
        'backward_pawns': backward_pawns,
        'pawns_by_file': pawns_by_file,
        'total_pawns': len(pawn_squares),
        'score': score
    }


def _get_pawns_by_file(pawn_squares: List[int]) -> Dict[int, List[int]]:
    """
    Group pawns by file.
    
    Args:
        pawn_squares: List of pawn square indices
    
    Returns:
        Dictionary mapping file index to list of pawn squares on that file
    """
    pawns_by_file = {i: [] for i in range(8)}
    
    for square in pawn_squares:
        file = chess.square_file(square)
        pawns_by_file[file].append(square)
    
    return pawns_by_file


def _count_pawn_islands(pawns_by_file: Dict[int, List[int]]) -> int:
    """
    Count pawn islands (groups of connected pawns).
    
    Args:
        pawns_by_file: Pawns grouped by file
    
    Returns:
        Number of pawn islands
    """
    islands = 0
    in_island = False
    
    for file in range(8):
        if pawns_by_file[file]:  # File has pawns
            if not in_island:
                islands += 1
                in_island = True
        else:  # File has no pawns
            in_island = False
    
    return islands


def _find_passed_pawns(board: chess.Board, color: chess.Color, pawn_squares: List[int]) -> List[int]:
    """
    Find passed pawns (no enemy pawns can block advancement).
    
    Args:
        board: Chess board
        color: Color to analyze
        pawn_squares: List of friendly pawn squares
    
    Returns:
        List of passed pawn squares
    """
    passed = []
    enemy_color = not color
    
    for square in pawn_squares:
        file = chess.square_file(square)
        rank = chess.square_rank(square)
        
        # Check if any enemy pawns can block this pawn
        is_passed = True
        
        # Check same file and adjacent files
        for check_file in [file - 1, file, file + 1]:
            if check_file < 0 or check_file > 7:
                continue
            
            # Check ranks ahead of this pawn
            if color == chess.WHITE:
                ranks_to_check = range(rank + 1, 8)
            else:
                ranks_to_check = range(0, rank)
            
            for check_rank in ranks_to_check:
                check_square = chess.square(check_file, check_rank)
                piece = board.piece_at(check_square)
                
                if piece and piece.piece_type == chess.PAWN and piece.color == enemy_color:
                    is_passed = False
                    break
            
            if not is_passed:
                break
        
        if is_passed:
            passed.append(square)
    
    return passed


def _count_doubled_pawns(pawns_by_file: Dict[int, List[int]]) -> int:
    """
    Count doubled pawns (multiple pawns on same file).
    
    Args:
        pawns_by_file: Pawns grouped by file
    
    Returns:
        Number of extra pawns (doubled/tripled)
    """
    doubled = 0
    
    for file in range(8):
        count = len(pawns_by_file[file])
        if count > 1:
            doubled += count - 1  # Count extras (2 pawns = 1 doubled, 3 = 2 doubled)
    
    return doubled


def _find_isolated_pawns(pawns_by_file: Dict[int, List[int]], pawn_squares: List[int]) -> List[int]:
    """
    Find isolated pawns (no friendly pawns on adjacent files).
    
    Args:
        pawns_by_file: Pawns grouped by file
        pawn_squares: List of pawn squares
    
    Returns:
        List of isolated pawn squares
    """
    isolated = []
    
    for square in pawn_squares:
        file = chess.square_file(square)
        
        # Check if adjacent files have friendly pawns
        has_support = False
        
        for adjacent_file in [file - 1, file + 1]:
            if adjacent_file < 0 or adjacent_file > 7:
                continue
            
            if pawns_by_file[adjacent_file]:
                has_support = True
                break
        
        if not has_support:
            isolated.append(square)
    
    return isolated


def _find_backward_pawns(board: chess.Board, color: chess.Color, 
                        pawn_squares: List[int], pawns_by_file: Dict[int, List[int]]) -> List[int]:
    """
    Find backward pawns (can't advance safely, no support from adjacent pawns).
    
    Args:
        board: Chess board
        color: Color to analyze
        pawn_squares: List of pawn squares
        pawns_by_file: Pawns grouped by file
    
    Returns:
        List of backward pawn squares
    """
    backward = []
    
    for square in pawn_squares:
        file = chess.square_file(square)
        rank = chess.square_rank(square)
        
        # Check if pawn can be supported by adjacent pawns
        can_be_supported = False
        
        for adjacent_file in [file - 1, file + 1]:
            if adjacent_file < 0 or adjacent_file > 7:
                continue
            
            # Check if any adjacent pawn is behind or equal to this one
            for adj_square in pawns_by_file[adjacent_file]:
                adj_rank = chess.square_rank(adj_square)
                
                # For white, supporting pawns should be at same rank or behind
                # For black, supporting pawns should be at same rank or behind (higher rank number)
                if color == chess.WHITE:
                    if adj_rank <= rank:
                        can_be_supported = True
                        break
                else:
                    if adj_rank >= rank:
                        can_be_supported = True
                        break
            
            if can_be_supported:
                break
        
        # If can't be supported and can't advance safely, it's backward
        if not can_be_supported:
            # Check if square in front is controlled by enemy
            if color == chess.WHITE and rank < 7:
                front_square = chess.square(file, rank + 1)
                if board.is_attacked_by(not color, front_square):
                    backward.append(square)
            elif color == chess.BLACK and rank > 0:
                front_square = chess.square(file, rank - 1)
                if board.is_attacked_by(not color, front_square):
                    backward.append(square)
    
    return backward


def _analyze_pawn_majorities(white_structure: Dict, black_structure: Dict, 
                            evidence: List[str], factors: List[Dict]):
    """
    Analyze pawn majorities on kingside and queenside.
    
    Args:
        white_structure: White pawn structure data
        black_structure: Black pawn structure data
        evidence: List to append evidence to
        factors: List to append factors to
    """
    # Count pawns on kingside (e-h files) and queenside (a-d files)
    white_kingside = sum(len(white_structure['pawns_by_file'][f]) for f in range(4, 8))
    white_queenside = sum(len(white_structure['pawns_by_file'][f]) for f in range(0, 4))
    
    black_kingside = sum(len(black_structure['pawns_by_file'][f]) for f in range(4, 8))
    black_queenside = sum(len(black_structure['pawns_by_file'][f]) for f in range(0, 4))
    
    # Kingside majority
    if white_kingside > black_kingside + 1:
        factors.append({
            'type': 'pawn_majority',
            'side': 'white',
            'wing': 'kingside',
            'advantage': white_kingside - black_kingside
        })
        evidence.append(f"White has kingside pawn majority ({white_kingside} vs {black_kingside})")
    elif black_kingside > white_kingside + 1:
        factors.append({
            'type': 'pawn_majority',
            'side': 'black',
            'wing': 'kingside',
            'advantage': black_kingside - white_kingside
        })
        evidence.append(f"Black has kingside pawn majority ({black_kingside} vs {white_kingside})")
    
    # Queenside majority
    if white_queenside > black_queenside + 1:
        factors.append({
            'type': 'pawn_majority',
            'side': 'white',
            'wing': 'queenside',
            'advantage': white_queenside - black_queenside
        })
        evidence.append(f"White has queenside pawn majority ({white_queenside} vs {black_queenside})")
    elif black_queenside > white_queenside + 1:
        factors.append({
            'type': 'pawn_majority',
            'side': 'black',
            'wing': 'queenside',
            'advantage': black_queenside - white_queenside
        })
        evidence.append(f"Black has queenside pawn majority ({black_queenside} vs {white_queenside})")
