"""
Move Comparison Analysis Tool
Compares multiple candidate moves using chess engine evaluation.

Returns structured analysis of:
- Best move alternatives
- Evaluation differences between moves
- Why certain moves are better/worse
- Tactical opportunities or blunders
"""

import chess
from typing import Dict, List, Optional
import sys
import os

# Import engine
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
from engine import StockfishEngine


def compare_moves(fen: str, moves: List[str], depth: int = 18) -> Dict:
    """
    Compare multiple candidate moves using engine evaluation.
    
    Args:
        fen: Position in FEN notation
        moves: List of moves to compare (in SAN or UCI notation)
        depth: Engine search depth (default: 18)
    
    Returns:
        Dictionary with:
            - score: Best evaluation found
            - factors: List of move comparisons and differences
            - evidence: Human-readable explanations
            - move_evals: List of evaluations for each move
    
    Example:
        >>> result = compare_moves("r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 3 3", 
        ...                        ["d4", "O-O", "Nc3"])
        >>> result['evidence']
        ['d4 is best (+0.45)', 'O-O is -0.15 worse', 'Nc3 is -0.30 worse']
    """
    board = chess.Board(fen)
    
    move_evals = []
    factors = []
    evidence = []
    
    # Initialize engine
    with StockfishEngine(depth=depth) as engine:
        # Get best move from engine for comparison
        best_moves = engine.get_best_moves(fen, n=1, depth=depth)
        best_eval = best_moves[0]['score'] if best_moves[0]['type'] == 'cp' else None
        best_move_san = best_moves[0]['san']
        
        # Evaluate each candidate move
        for move_str in moves:
            try:
                # Try to parse move (accept both SAN and UCI)
                try:
                    move = board.parse_san(move_str)
                except:
                    move = chess.Move.from_uci(move_str)
                
                if move not in board.legal_moves:
                    move_evals.append({
                        'move': move_str,
                        'legal': False,
                        'eval': None,
                        'type': None
                    })
                    evidence.append(f"{move_str} is ILLEGAL")
                    continue
                
                # Make the move and evaluate resulting position
                board_copy = board.copy()
                board_copy.push(move)
                
                result_eval = engine.get_eval(board_copy.fen(), depth=depth)
                
                # Negate evaluation since we're evaluating opponent's position
                if result_eval['type'] == 'cp':
                    eval_score = -result_eval['score']
                    eval_type = 'cp'
                else:
                    eval_score = -result_eval['score'] if result_eval['score'] else 0
                    eval_type = 'mate'
                
                move_evals.append({
                    'move': move_str,
                    'san': board.san(move),
                    'uci': move.uci(),
                    'legal': True,
                    'eval': eval_score,
                    'type': eval_type
                })
                
            except Exception as e:
                move_evals.append({
                    'move': move_str,
                    'legal': False,
                    'error': str(e),
                    'eval': None,
                    'type': None
                })
                evidence.append(f"{move_str} - Error: {e}")
        
        # Sort moves by evaluation (best first)
        legal_moves = [m for m in move_evals if m['legal'] and m['type'] == 'cp']
        legal_moves.sort(key=lambda x: x['eval'], reverse=True)
        
        # Build comparison evidence
        if legal_moves:
            best_candidate = legal_moves[0]
            best_candidate_eval = best_candidate['eval'] / 100
            
            # Compare to engine's best move
            if best_eval is not None:
                engine_best_eval = best_eval / 100
                
                if best_candidate['san'] == best_move_san:
                    evidence.append(f"✓ Best candidate {best_candidate['san']} matches engine's top choice ({engine_best_eval:+.2f})")
                    factors.append({
                        'type': 'matches_engine_best',
                        'move': best_candidate['san'],
                        'eval': best_candidate_eval
                    })
                else:
                    diff = engine_best_eval - best_candidate_eval
                    evidence.append(f"Engine prefers {best_move_san} ({engine_best_eval:+.2f}) over {best_candidate['san']} ({best_candidate_eval:+.2f}) by {diff:.2f} pawns")
                    factors.append({
                        'type': 'engine_suggests_better',
                        'engine_move': best_move_san,
                        'engine_eval': engine_best_eval,
                        'candidate_move': best_candidate['san'],
                        'candidate_eval': best_candidate_eval,
                        'difference': diff
                    })
            
            # Compare candidate moves to each other
            for i, move_data in enumerate(legal_moves):
                move_eval = move_data['eval'] / 100
                
                if i == 0:
                    evidence.append(f"1. {move_data['san']} is best among candidates ({move_eval:+.2f})")
                else:
                    diff = legal_moves[0]['eval'] - move_data['eval']
                    diff_pawns = diff / 100
                    
                    # Classify difference
                    if diff < 25:
                        category = "roughly equal"
                    elif diff < 50:
                        category = "slightly worse"
                    elif diff < 100:
                        category = "worse"
                    elif diff < 200:
                        category = "significantly worse"
                    else:
                        category = "much worse"
                    
                    evidence.append(f"{i + 1}. {move_data['san']} is {category} ({move_eval:+.2f}, -{diff_pawns:.2f})")
                    
                    factors.append({
                        'type': 'move_comparison',
                        'move': move_data['san'],
                        'eval': move_eval,
                        'difference_from_best': diff_pawns,
                        'category': category
                    })
        
        # Add tactical observations
        _add_tactical_observations(board, legal_moves, evidence, factors)
    
    best_score = legal_moves[0]['eval'] if legal_moves else 0
    
    return {
        'score': best_score,
        'factors': factors,
        'evidence': evidence,
        'move_evals': move_evals,
        'engine_best_move': best_move_san if best_eval is not None else None,
        'engine_best_eval': best_eval
    }


def analyze_move_quality(fen: str, played_move: str, depth: int = 18) -> Dict:
    """
    Analyze the quality of a move that was played.
    
    This function compares:
    1. The evaluation BEFORE the move (from perspective of side to move)
    2. The evaluation AFTER the move (negated, since opponent is now to move)
    3. The engine's best move evaluation
    
    A blunder is detected either by:
    - Large difference from engine's best move (>3 pawns)
    - Large swing in position evaluation (losing a winning position, etc.)
    
    Args:
        fen: Position BEFORE the move (in FEN notation)
        played_move: Move that was played (in SAN or UCI notation)
        depth: Engine search depth (default: 18)
    
    Returns:
        Dictionary with:
            - score: Evaluation loss/gain from this move
            - factors: Classification of move quality
            - evidence: Human-readable explanation
            - alternatives: Better move suggestions if applicable
            - eval_before: Position eval before move
            - eval_after: Position eval after move
            - eval_swing: How much the position changed
    """
    board = chess.Board(fen)
    
    factors = []
    evidence = []
    alternatives = []
    
    with StockfishEngine(depth=depth) as engine:
        # Get evaluation BEFORE the move (from side to move's perspective)
        eval_before_result = engine.get_eval(fen, depth=depth)
        if eval_before_result['type'] == 'cp':
            eval_before = eval_before_result['score']
        elif eval_before_result['type'] == 'mate':
            # Convert mate to large centipawn value
            mate_score = eval_before_result['score']
            eval_before = 10000 if mate_score > 0 else -10000
        else:
            eval_before = 0
        
        # Get engine's best moves
        best_moves = engine.get_best_moves(fen, n=5, depth=depth)
        
        if not best_moves:
            return {
                'score': 0,
                'factors': [],
                'evidence': ['Unable to analyze position'],
                'alternatives': [],
                'eval_before': eval_before,
                'eval_after': None,
                'eval_swing': 0
            }
        
        engine_best_eval = best_moves[0]['score'] if best_moves[0]['type'] == 'cp' else (10000 if best_moves[0].get('score', 0) > 0 else -10000)
        engine_best_move = best_moves[0]['san']
        
        # Parse and validate played move
        try:
            try:
                move = board.parse_san(played_move)
            except:
                move = chess.Move.from_uci(played_move)
            
            played_san = board.san(move)
            
            if move not in board.legal_moves:
                return {
                    'score': -999,
                    'factors': [{'type': 'illegal_move'}],
                    'evidence': [f"{played_move} is ILLEGAL in this position!"],
                    'alternatives': [engine_best_move],
                    'eval_before': eval_before,
                    'eval_after': None,
                    'eval_swing': 0
                }
            
            # Evaluate position AFTER played move
            board.push(move)
            played_eval_result = engine.get_eval(board.fen(), depth=depth)
            
            # Negate because it's now opponent's turn
            if played_eval_result['type'] == 'cp':
                eval_after = -played_eval_result['score']
            elif played_eval_result['type'] == 'mate':
                mate_score = played_eval_result['score']
                eval_after = -10000 if mate_score > 0 else 10000
            else:
                eval_after = 0
            
            # Calculate evaluation swing (how much the position changed)
            eval_swing = eval_before - eval_after
            eval_swing_pawns = eval_swing / 100
            
            # Calculate loss from best move
            eval_loss = engine_best_eval - eval_after
            eval_loss_pawns = eval_loss / 100
            
            # Convert to pawns for display
            eval_before_pawns = eval_before / 100
            eval_after_pawns = eval_after / 100
            engine_best_pawns = engine_best_eval / 100
            
            # Classify move quality based on BOTH eval loss AND eval swing
            # A move can be a blunder if it swings the eval dramatically
            if played_san == engine_best_move:
                category = "best"
                evidence.append(f"{played_san} is the engine's top choice!")
                factors.append({
                    'type': 'move_quality',
                    'category': 'best',
                    'eval_loss': 0,
                    'eval_swing': eval_swing_pawns
                })
            elif eval_loss < 10 and abs(eval_swing) < 30:
                category = "excellent"
                evidence.append(f"{played_san} is an excellent move")
                factors.append({
                    'type': 'move_quality',
                    'category': 'excellent',
                    'eval_loss': eval_loss_pawns,
                    'eval_swing': eval_swing_pawns
                })
            elif eval_loss < 30 and abs(eval_swing) < 50:
                category = "good"
                evidence.append(f"{played_san} is a good move")
                factors.append({
                    'type': 'move_quality',
                    'category': 'good',
                    'eval_loss': eval_loss_pawns,
                    'eval_swing': eval_swing_pawns
                })
                alternatives = [engine_best_move]
            elif eval_loss < 100 and abs(eval_swing) < 150:
                category = "inaccuracy"
                evidence.append(f"{played_san} is an INACCURACY (loses ~{eval_loss_pawns:.1f} pawns vs best)")
                factors.append({
                    'type': 'move_quality',
                    'category': 'inaccuracy',
                    'eval_loss': eval_loss_pawns,
                    'eval_swing': eval_swing_pawns
                })
                alternatives = [m['san'] for m in best_moves[:3]]
                evidence.append(f"Better moves: {', '.join(alternatives)}")
            elif eval_loss < 300 and abs(eval_swing) < 400:
                category = "mistake"
                evidence.append(f"{played_san} is a MISTAKE (loses ~{eval_loss_pawns:.1f} pawns)")
                factors.append({
                    'type': 'move_quality',
                    'category': 'mistake',
                    'eval_loss': eval_loss_pawns,
                    'eval_swing': eval_swing_pawns
                })
                alternatives = [m['san'] for m in best_moves[:3]]
                evidence.append(f"Much better: {', '.join(alternatives)}")
            else:
                category = "blunder"
                evidence.append(f"{played_san} is a BLUNDER! (loses ~{eval_loss_pawns:.1f} pawns)")
                factors.append({
                    'type': 'move_quality',
                    'category': 'blunder',
                    'eval_loss': eval_loss_pawns,
                    'eval_swing': eval_swing_pawns
                })
                alternatives = [m['san'] for m in best_moves[:3]]
                evidence.append(f"Should have played: {', '.join(alternatives)}")
            
            # Add evaluation context showing before/after
            evidence.append(f"Position eval before: {eval_before_pawns:+.2f}")
            evidence.append(f"Position eval after {played_san}: {eval_after_pawns:+.2f}")
            evidence.append(f"Eval swing: {eval_swing_pawns:+.2f} pawns")
            if played_san != engine_best_move:
                evidence.append(f"Best move {engine_best_move} would give: {engine_best_pawns:+.2f}")
            
            return {
                'score': -eval_loss,
                'factors': factors,
                'evidence': evidence,
                'alternatives': alternatives,
                'played_eval': eval_after,
                'best_eval': engine_best_eval,
                'eval_loss': eval_loss,
                'eval_before': eval_before,
                'eval_after': eval_after,
                'eval_swing': eval_swing,
                'category': category
            }
            
        except Exception as e:
            return {
                'score': 0,
                'factors': [{'type': 'error', 'message': str(e)}],
                'evidence': [f"Error analyzing move '{played_move}': {e}"],
                'alternatives': [],
                'eval_before': eval_before,
                'eval_after': None,
                'eval_swing': 0
            }
    
    return {
        'score': 0,
        'factors': [],
        'evidence': ['Unable to complete analysis'],
        'alternatives': [],
        'eval_before': 0,
        'eval_after': None,
        'eval_swing': 0
    }


def _add_tactical_observations(board: chess.Board, legal_moves: List[Dict], 
                               evidence: List[str], factors: List[Dict]):
    """
    Add tactical observations about the moves.
    
    Args:
        board: Chess board
        legal_moves: List of evaluated moves
        evidence: List to append evidence to
        factors: List to append factors to
    """
    if not legal_moves:
        return
    
    # Check if best move gives check
    best_move_san = legal_moves[0]['san']
    
    if '+' in best_move_san or '#' in best_move_san:
        factors.append({
            'type': 'tactical_feature',
            'feature': 'check',
            'move': best_move_san
        })
        evidence.append(f"Best move {best_move_san} gives check")
    
    # Check for captures
    if 'x' in best_move_san:
        factors.append({
            'type': 'tactical_feature',
            'feature': 'capture',
            'move': best_move_san
        })
        evidence.append(f"Best move {best_move_san} captures material")
