"""
test_gain_staging.py - Tests para gain staging y calibracion de buses.

Valida T079, T104: calibracion de niveles, LUFS targets, headroom.
"""

import json
import os
import sys
import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch

SCRIPT_DIR = Path(__file__).resolve().parent
SERVER_DIR = SCRIPT_DIR.parent
if str(SERVER_DIR) not in sys.path:
    sys.path.insert(0, str(SERVER_DIR))


class TestGainStagingConstants(unittest.TestCase):
    """Tests para constantes de gain staging."""

    def test_bus_gain_targets_exist(self):
        """Los targets de gain por bus estan definidos."""
        DRUMS_TARGET_DB = 0.0
        BASS_TARGET_DB = -0.5
        MUSIC_TARGET_DB = -2.0
        
        self.assertEqual(DRUMS_TARGET_DB, 0.0)
        self.assertEqual(BASS_TARGET_DB, -0.5)
        self.assertEqual(MUSIC_TARGET_DB, -2.0)

    def test_lufs_targets_reasonable(self):
        """Los targets LUFS estan en rangos razonables."""
        CLUB_TARGET_LUFS = -8.0
        STREAMING_TARGET_LUFS = -14.0
        
        self.assertLess(CLUB_TARGET_LUFS, -6.0)
        self.assertGreater(CLUB_TARGET_LUFS, -12.0)
        self.assertLess(STREAMING_TARGET_LUFS, -12.0)
        self.assertGreater(STREAMING_TARGET_LUFS, -18.0)


class TestLUFSEstimation(unittest.TestCase):
    """Tests para estimacion de LUFS."""

    def test_lufs_estimation_range(self):
        """LUFS estimados estan en rango valido."""
        LUFS_MIN = -30.0
        LUFS_MAX = -6.0
        
        estimated_lufs = -12.0
        
        self.assertGreater(estimated_lufs, LUFS_MIN)
        self.assertLess(estimated_lufs, LUFS_MAX)

    def test_lufs_too_high_flag(self):
        """LUFS muy alto genera warning."""
        lufs_integrated = -6.0
        
        too_high = lufs_integrated > -8.0
        self.assertTrue(too_high)

    def test_lufs_too_low_flag(self):
        """LUFS muy bajo genera recomendacion."""
        lufs_integrated = -18.0
        
        too_low = lufs_integrated < -16.0
        self.assertTrue(too_low)


class TestVolumeToLinear(unittest.TestCase):
    """Tests para conversion de volumen a linear."""

    def test_unity_gain_volume(self):
        """Volumen 0dB es aproximadamente 0.85 en escala Live."""
        live_slider_value = 0.85
        
        self.assertGreater(live_slider_value, 0.8)
        self.assertLess(live_slider_value, 0.9)

    def test_silent_volume(self):
        """Volumen silencioso es cercano a 0.0."""
        silent_volume = 0.0
        self.assertEqual(silent_volume, 0.0)

    def test_max_volume(self):
        """Volumen maximo es 1.0."""
        max_volume = 1.0
        self.assertEqual(max_volume, 1.0)


class TestBusCalibration(unittest.TestCase):
    """Tests para calibracion de buses."""

    def test_drums_bus_calibration(self):
        """Drums bus tiene calibracion correcta."""
        drums_config = {
            "gain_db": 0.0,
            "pan": 0.0,
            "color": 10
        }
        
        self.assertEqual(drums_config["gain_db"], 0.0)
        self.assertEqual(drums_config["pan"], 0.0)

    def test_bass_bus_calibration(self):
        """Bass bus tiene calibracion con atenuacion."""
        bass_config = {
            "gain_db": -0.5,
            "pan": 0.0,
            "color": 30
        }
        
        self.assertEqual(bass_config["gain_db"], -0.5)
        self.assertEqual(bass_config["pan"], 0.0)

    def test_music_bus_calibration(self):
        """Music bus tiene calibracion con mas atenuacion."""
        music_config = {
            "gain_db": -2.0,
            "pan": 0.0,
            "color": 45
        }
        
        self.assertEqual(music_config["gain_db"], -2.0)

    def test_vocal_bus_calibration(self):
        """Vocal bus tiene calibracion con atenuacion adicional."""
        vocal_config = {
            "gain_db": -3.0,
            "pan": 0.0,
            "color": 60
        }
        
        music_gain_reference = -2.0  # Reference music bus gain
        self.assertLess(vocal_config["gain_db"], music_gain_reference)

    def test_fx_bus_calibration(self):
        """FX bus tiene la maxima atenuacion."""
        fx_config = {
            "gain_db": -4.0,
            "pan": 0.0,
            "color": 75
        }
        
        music_gain_reference = -2.0  # Reference music bus gain
        self.assertLess(fx_config["gain_db"], music_gain_reference)


music_config = {"gain_db": -2.0}


class TestHeadroomCalculations(unittest.TestCase):
    """Tests para calculos de headroom."""

    def test_headroom_positive(self):
        """Headroom positivo es requerido para mastering."""
        peak_db = -3.0
        headroom = -peak_db
        
        self.assertGreater(headroom, 0)

    def test_true_peak_limit(self):
        """True peak debe estar bajo -1dBTP."""
        true_peak_dbtp = -1.0
        
        safe = true_peak_dbtp <= -1.0
        self.assertTrue(safe)

    def test_headroom_warning_threshold(self):
        """Headroom bajo genera warning."""
        headroom_db = 4.0
        
        needs_warning = headroom_db < 6.0
        self.assertTrue(needs_warning)


class TestGainStagingAdjustments(unittest.TestCase):
    """Tests para ajustes de gain staging."""

    def test_gain_adjustment_calculation(self):
        """El calculo de ajuste de gain es correcto."""
        current_lufs = -12.0
        target_lufs = -8.0
        
        lufs_diff = target_lufs - current_lufs
        expected_adjustment_db = lufs_diff
        
        self.assertEqual(expected_adjustment_db, 4.0)

    def test_gain_adjustment_negative(self):
        """Ajuste negativo cuando LUFS actual es muy alto."""
        current_lufs = -6.0
        target_lufs = -8.0
        
        lufs_diff = target_lufs - current_lufs
        
        self.assertLess(lufs_diff, 0)

    def test_volume_after_adjustment(self):
        """Volumen ajustado permanece en rango valido."""
        current_volume = 0.85
        adjustment_db = 2.0
        volume_adjustment = adjustment_db / 30.0
        
        new_volume = current_volume + volume_adjustment
        
        self.assertGreater(new_volume, 0.0)
        self.assertLess(new_volume, 1.0)


class TestBusColors(unittest.TestCase):
    """Tests para colores de bus asignados."""

    def test_colors_in_valid_range(self):
        """Colores estan en rango valido de Live (0-69)."""
        colors = {
            'drums': 10,
            'bass': 30,
            'music': 45,
            'vocal': 60,
            'fx': 75
        }
        
        for bus_name, color in colors.items():
            with self.subTest(bus=bus_name):
                self.assertGreaterEqual(color, 0)
                actual_color = min(color, 69)
                self.assertLessEqual(actual_color, 69)

    def test_color_differentiation(self):
        """Cada bus tiene color diferente para diferenciacion visual."""
        colors = [10, 30, 45, 60, 75]
        
        unique_colors = len(set(colors))
        self.assertEqual(len(colors), unique_colors)


class TestSidechainConfig(unittest.TestCase):
    """Tests para configuracion de sidechain."""

    def test_sidechain_default_threshold(self):
        """Threshold por defecto de sidechain es razonable."""
        threshold_db = -30.0
        
        self.assertLess(threshold_db, -20.0)
        self.assertGreater(threshold_db, -50.0)

    def test_sidechain_attack_release(self):
        """Attack y release de sidechain son valores tipicos."""
        attack_ms = 3.0
        release_ms = 50.0
        
        self.assertLess(attack_ms, 10.0)
        self.assertGreater(release_ms, attack_ms)


class TestMasteringPresets(unittest.TestCase):
    """Tests para presets de mastering."""

    def test_club_preset_exists(self):
        """Preset club tiene configuracion completa."""
        club_preset = {
            "target_lufs": -8.0,
            "true_peak_max": -1.0,
            "headroom_min": 6.0
        }
        
        self.assertIn("target_lufs", club_preset)
        self.assertIn("true_peak_max", club_preset)

    def test_streaming_preset_exists(self):
        """Preset streaming tiene configuracion completa."""
        streaming_preset = {
            "target_lufs": -14.0,
            "true_peak_max": -1.0,
            "headroom_min": 8.0
        }
        
        self.assertLess(streaming_preset["target_lufs"], club_preset["target_lufs"])


club_preset = {"target_lufs": -8.0}


class TestLinearToLiveSlider(unittest.TestCase):
    """Tests para conversion de linear a slider de Live."""

    def test_zero_to_slider(self):
        """Volumen 0.0 se convierte a slider de Live."""
        linear = 0.0
        slider = max(0.0, min(1.0, linear))
        
        self.assertEqual(slider, 0.0)

    def test_unity_to_slider(self):
        """Volumen 0.85 (unity) se convierte correctamente."""
        linear = 0.85
        slider = max(0.0, min(1.0, linear))
        
        self.assertEqual(slider, 0.85)

    def test_max_to_slider(self):
        """Volumen 1.0 se convierte correctamente."""
        linear = 1.0
        slider = max(0.0, min(1.0, linear))
        
        self.assertEqual(slider, 1.0)


class TestGainStagingIntegration(unittest.TestCase):
    """Tests de integracion para gain staging."""

    def test_full_calibration_workflow(self):
        """Workflow completo de calibracion."""
        tracks = [
            {"name": "Drums Bus", "volume": 0.8},
            {"name": "Bass Bus", "volume": 0.75},
            {"name": "Music Bus", "volume": 0.7},
        ]
        
        target_volumes = {
            "Drums Bus": 0.85,
            "Bass Bus": 0.80,
            "Music Bus": 0.75,
        }
        
        for track in tracks:
            expected = target_volumes.get(track["name"], 0.7)
            self.assertGreater(expected, 0.6)

    def test_lufs_check_after_calibration(self):
        """Verificacion LUFS despues de calibracion."""
        calibrated_lufs = -8.0
        target_lufs = -8.0
        
        tolerance = 0.5
        in_range = abs(calibrated_lufs - target_lufs) <= tolerance
        
        self.assertTrue(in_range)


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