import numpy as np
from scipy.signal import iirnotch, sosfilt, butter
from .base import BaseProcessorModule

class SpectralSmootherModule(BaseProcessorModule):
    """Surgical Dynamic Spectral Smoother with Anti-Aliasing logic."""
    
    def process(self, audio: np.ndarray, resonances: list = None, threshold_db: float = -20.0, sensitivity: float = 0.5, key_freq: float = None) -> np.ndarray:
        if not resonances: return audio
        
        sr = self.sample_rate
        out = audio.copy()
        
        for res in resonances:
            freq = res['freq']
            # Anti-Aliasing Check: 
            # If a peak is NOT a multiple of our root key freq, it is likely an artifact.
            is_musical = False
            if key_freq:
                ratio = freq / key_freq
                # Check if it is close to an integer multiple (1st, 2nd, 3rd harmonic...)
                if abs(ratio - round(ratio)) < 0.05:
                    is_musical = True
            
            # We are much more aggressive with non-musical peaks
            prom_threshold = 6.0 if not is_musical else 12.0
            if res.get('prominence', 0) < prom_threshold: continue
            
            bw = freq / 30.0 # Even narrower for aliasing
            sos_bp = butter(2, [max(20, freq-bw), min(sr/2-1, freq+bw)], 'bandpass', fs=sr, output='sos')
            
            def process_chan(chan):
                band_energy = sosfilt(sos_bp, chan)
                rms_db = 20 * np.log10(np.sqrt(np.mean(band_energy**2)) + 1e-9)
                
                if rms_db > threshold_db:
                    # Dynamic reduction scaling
                    reduction_mult = 1.5 if not is_musical else 0.8
                    reduction_db = (rms_db - threshold_db) * sensitivity * reduction_mult
                    
                    sos_stop = butter(2, [max(20, freq-bw), min(sr/2-1, freq+bw)], 'bandstop', fs=sr, output='sos')
                    notched = sosfilt(sos_stop, chan)
                    blend = min(0.9, reduction_db / 15.0)
                    return (notched * blend) + (chan * (1.0 - blend))
                return chan

            if out.ndim == 1: out = process_chan(out)
            else:
                out[:, 0] = process_chan(out[:, 0]); out[:, 1] = process_chan(out[:, 1])
                
        return out