"""
Test per verificare che il differ non duplichi l'analisi dei file.
Verifica che countings e metrics vengano calcolati una sola volta.
"""
import os
import tempfile
import shutil
from pathlib import Path
from unittest.mock import patch, MagicMock
import pytest

from pyucc.core.differ import BaselineManager, Differ


def test_differ_no_duplicate_analysis(tmp_path):
    """Verifica che countings e metrics vengano calcolati solo una volta per file."""
    
    # Crea una directory temporanea con alcuni file Python
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    
    # Crea file di test
    file1 = project_dir / "test1.py"
    file1.write_text("def foo():\n    return 42\n")
    
    file2 = project_dir / "test2.py"
    file2.write_text("def bar():\n    x = 1\n    return x\n")
    
    # Crea baseline manager con baselines_root temporanea
    baseline_dir = tmp_path / "baselines"
    baseline_dir.mkdir()
    bm = BaselineManager(str(project_dir), baselines_root=str(baseline_dir))
    baseline_id = bm.create_baseline_from_dir(
        str(project_dir),
        baseline_id="test_baseline",
        snapshot=True,
        compute_sha1=True
    )
    
    # Carica metadata
    metadata = bm.load_metadata(baseline_id)
    
    # Verifica che la baseline abbia countings e metrics
    assert len(metadata.files) == 2
    for fm in metadata.files:
        # Baseline deve avere countings e metrics già calcolati
        assert fm.countings is not None, f"File {fm.path} dovrebbe avere countings"
        assert fm.metrics is not None, f"File {fm.path} dovrebbe avere metrics"
    
    # Modifica un file
    file1.write_text("def foo():\n    # nuovo commento\n    return 42\n")
    
    # Crea differ con mock per contare quante volte vengono chiamate le funzioni di analisi
    # Le funzioni sono importate dentro build_current_file_list, quindi devo mockare i moduli originali
    with patch('pyucc.core.countings_impl.analyze_file_counts') as mock_counts, \
         patch('pyucc.core.metrics.analyze_file_metrics') as mock_metrics:
        
        # Configura i mock per restituire valori realistici
        mock_counts.return_value = {
            'physical_lines': 3,
            'code_lines': 2,
            'comment_lines': 1,
            'blank_lines': 0
        }
        mock_metrics.return_value = {
            'avg_cc': 1.0,
            'max_cc': 1,
            'func_count': 1,
            'mi': 100.0
        }
        
        # Esegui differ
        differ = Differ(metadata, str(project_dir))
        current_files = differ.build_current_file_list()
        
        # Verifica che build_current_file_list abbia chiamato le funzioni di analisi
        # Dovrebbe essere chiamato una volta per ogni file (2 file)
        assert mock_counts.call_count == 2, \
            f"analyze_file_counts dovrebbe essere chiamato 2 volte, invece: {mock_counts.call_count}"
        assert mock_metrics.call_count == 2, \
            f"analyze_file_metrics dovrebbe essere chiamato 2 volte, invece: {mock_metrics.call_count}"
        
        # Verifica che i file correnti abbiano countings e metrics
        for fm in current_files:
            assert fm.countings is not None, f"File {fm.path} dovrebbe avere countings"
            assert fm.metrics is not None, f"File {fm.path} dovrebbe avere metrics"
        
        # Reset dei mock per verificare che diff() non richiami le funzioni
        mock_counts.reset_mock()
        mock_metrics.reset_mock()
        
        # Esegui diff
        result = differ.diff()
        
        # VERIFICA CRITICA: diff() non dovrebbe richiamare analyze_file_counts o analyze_file_metrics
        # perché i dati sono già presenti nei FileMeta objects
        assert mock_counts.call_count == 0, \
            f"diff() NON dovrebbe chiamare analyze_file_counts, invece chiamato {mock_counts.call_count} volte"
        assert mock_metrics.call_count == 0, \
            f"diff() NON dovrebbe chiamare analyze_file_metrics, invece chiamato {mock_metrics.call_count} volte"
        
        # Verifica che il risultato contenga i dati corretti
        assert 'pairs' in result
        assert len(result['pairs']) == 2
        
        for pair in result['pairs']:
            # Ogni pair dovrebbe avere countings e metrics delta
            assert 'baseline_countings' in pair
            assert 'current_countings' in pair
            assert 'baseline_metrics' in pair
            assert 'current_metrics' in pair


def test_differ_uses_precomputed_data():
    """Verifica che il differ usi i dati pre-calcolati invece di ricalcolarli."""
    
    with tempfile.TemporaryDirectory() as tmpdir:
        project_dir = Path(tmpdir) / "project"
        project_dir.mkdir()
        
        # Crea file
        test_file = project_dir / "example.py"
        test_file.write_text("def test():\n    pass\n")
        
        # Crea baseline
        bm = BaselineManager(str(project_dir))
        baseline_id = bm.create_baseline_from_dir(str(project_dir))
        metadata = bm.load_metadata(baseline_id)
        
        # Verifica struttura baseline
        assert len(metadata.files) == 1
        baseline_file = metadata.files[0]
        assert baseline_file.countings is not None
        assert baseline_file.metrics is not None
        
        # Salva i valori originali
        original_countings = baseline_file.countings.copy()
        original_metrics = baseline_file.metrics.copy()
        
        # Modifica il file
        test_file.write_text("def test():\n    # comment\n    pass\n")
        
        # Crea differ e ottieni file correnti
        differ = Differ(metadata, str(project_dir))
        current_files = differ.build_current_file_list()
        
        # Verifica che il file corrente abbia countings e metrics
        assert len(current_files) == 1
        current_file = current_files[0]
        assert current_file.countings is not None
        assert current_file.metrics is not None
        
        # Esegui diff
        result = differ.diff()
        
        # Verifica che il risultato contenga i delta
        assert len(result['pairs']) == 1
        pair = result['pairs'][0]
        
        # Verifica che abbia sia baseline che current data
        assert pair['baseline_countings'] == original_countings
        assert pair['current_countings'] == current_file.countings
        
        # Verifica che i delta siano calcolati
        assert pair['countings_delta'] is not None
        assert 'code_lines' in pair['countings_delta']
        assert 'comment_lines' in pair['countings_delta']


if __name__ == '__main__':
    pytest.main([__file__, '-v'])
