"""
test_human_feel.py - Tests para HumanFeelEngine
T101-T103: Unit tests
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import unittest
from human_feel import HumanFeelEngine


class TestHumanFeelEngine(unittest.TestCase):
    """Tests para HumanFeelEngine"""

    def setUp(self):
        self.engine = HumanFeelEngine(seed=42)

    def test_timing_variation_range(self):
        """T040: Timing variation dentro de rango ±5ms."""
        notes = [{'pitch': 60, 'start': 0.0, 'velocity': 100}]
        result = self.engine.apply_timing_variation(notes, amount_ms=5.0)

        for note in result:
            offset_ms = (note['start'] - 0.0) * 1000
            self.assertGreaterEqual(offset_ms, -5.0)
            self.assertLessEqual(offset_ms, 5.0)

    def test_velocity_humanize_variance(self):
        """T041: Velocity variation ±5%."""
        notes = [{'pitch': 60, 'start': 0.0, 'velocity': 100}]
        result = self.engine.apply_velocity_humanize(notes, variance=0.05)

        for note in result:
            # Velocity debe estar en rango 95-105
            self.assertGreaterEqual(note['velocity'], 95)
            self.assertLessEqual(note['velocity'], 105)

    def test_note_skip_probability(self):
        """T042: Probabilidad de skip ~2%."""
        notes = [{'pitch': 60, 'start': float(i), 'velocity': 100} for i in range(100)]
        result = self.engine.apply_note_skip_probability(notes, prob=0.02)

        # Con seed=42, debe mantener aprox 98% de notas
        self.assertGreater(len(result), 90)  # No muy estricto por randomness
        self.assertLess(len(result), 100)

    def test_section_dynamics_scale(self):
        """T047-T050: Dinámica por sección."""
        notes = [{'pitch': 60, 'start': 0.0, 'velocity': 100}]

        # Intro = 70%
        intro_notes = self.engine.apply_section_dynamics(notes, 'intro')
        self.assertEqual(intro_notes[0]['velocity'], 70)

        # Drop = 100%
        drop_notes = self.engine.apply_section_dynamics(notes, 'drop')
        self.assertEqual(drop_notes[0]['velocity'], 100)

        # Build = 85%
        build_notes = self.engine.apply_section_dynamics(notes, 'build')
        self.assertEqual(build_notes[0]['velocity'], 85)

    def test_groove_applies_to_offbeat(self):
        """T044-T046: Groove aplica a notas off-beat."""
        # Nota en off-beat (beat position 0.5)
        notes = [{'pitch': 60, 'start': 4.5, 'velocity': 100}]
        result = self.engine.apply_groove(notes, style='shuffle', amount=1.0)

        # Debe tener delay aplicado
        self.assertGreater(result[0]['start'], 4.5)


if __name__ == '__main__':
    unittest.main()
