import os
import tempfile
import numpy as np
import soundfile as sf
from pydub import AudioSegment
from app.core.dsp_utils import find_nearest_zero_crossing_file, apply_micro_fade

def edit_audio_file(config: dict, input_path: str, output_path: str):
    """
    Applies editing commands on the audio file based on config:
    - cut_start_ms, cut_end_ms (with optional zero_crossing_align)
    - loop_count
    - fade_in_ms, fade_out_ms
    - volume_change_db
    """
    # Load original audio
    audio = AudioSegment.from_file(input_path)
    
    # Calculate start and end positions in ms
    cut_start_ms = config.get("cut_start_ms")
    cut_end_ms = config.get("cut_end_ms")
    
    total_len = len(audio)
    
    start_ms = float(cut_start_ms) if cut_start_ms is not None else 0.0
    end_ms = float(cut_end_ms) if cut_end_ms is not None else float(total_len)
    
    # Bound start and end
    start_ms = max(0.0, min(start_ms, float(total_len)))
    end_ms = max(start_ms, min(end_ms, float(total_len)))
    
    # Align to Zero-crossing if requested
    if config.get("zero_crossing_align", True):
        # Align start
        if cut_start_ms is not None:
            start_sec = start_ms / 1000.0
            aligned_start_sec = find_nearest_zero_crossing_file(input_path, start_sec)
            start_ms = aligned_start_sec * 1000.0
            
        # Align end
        if cut_end_ms is not None:
            end_sec = end_ms / 1000.0
            aligned_end_sec = find_nearest_zero_crossing_file(input_path, end_sec)
            end_ms = aligned_end_sec * 1000.0
            
    # Crop the segment
    segment = audio[start_ms:end_ms]
    
    # Apply micro-fade (50ms) to ensure smooth boundaries
    segment = apply_micro_fade(segment, fade_duration_ms=50)
    
    # Loop the segment
    loop_count = int(config.get("loop_count", 1))
    if loop_count > 1:
        segment = segment * loop_count
        
    # Apply volume change
    volume_change_db = float(config.get("volume_change_db", 0.0))
    if volume_change_db != 0.0:
        segment = segment + volume_change_db
        
    # Apply custom fades
    fade_in_ms = float(config.get("fade_in_ms", 0.0))
    if fade_in_ms > 0:
        segment = segment.fade_in(int(fade_in_ms))
        
    # Apply fade out
    fade_out_ms = float(config.get("fade_out_ms", 0.0))
    if fade_out_ms > 0:
        segment = segment.fade_out(int(fade_out_ms))
        
    # Ensure parent output directory exists
    os.makedirs(os.path.dirname(output_path), exist_ok=True)
    
    # Export as WAV
    segment.export(output_path, format="wav")
    
    return {
        "success": True,
        "output_path": output_path,
        "duration_ms": len(segment),
        "aligned_start_ms": round(start_ms, 2),
        "aligned_end_ms": round(end_ms, 2)
    }

def cut_and_loop_segment(
    file_path: str, 
    start_sec: float, 
    end_sec: float, 
    loop_count: int = 1,
    fade_in_ms: int = 50, 
    fade_out_ms: int = 50,
    volume_db_change: float = 0.0
) -> AudioSegment:
    """
    Cắt đoạn âm thanh, áp dụng gain, fade-in/out và lặp lại đoạn đó.
    pydub sử dụng mili-giây (ms) làm đơn vị chuẩn.
    
    Args:
        file_path: Đường dẫn tệp âm thanh
        start_sec: Thời điểm bắt đầu (giây)
        end_sec: Thời điểm kết thúc (giây)
        loop_count: Số lần lặp
        fade_in_ms: Thời gian fade-in (ms)
        fade_out_ms: Thời gian fade-out (ms)
        volume_db_change: Thay đổi âm lượng (dB)
        
    Returns:
        AudioSegment: Đoạn âm thanh đã xử lý
    """
    # Tải tệp tin âm thanh gốc
    sound = AudioSegment.from_file(file_path)
    
    # Chuyển đổi giây sang mili-giây
    start_ms = int(start_sec * 1000)
    end_ms = int(end_sec * 1000)
    
    # Trích xuất phân đoạn (Slicing)
    clip = sound[start_ms:end_ms]
    
    # Thay đổi âm lượng nếu có yêu cầu
    if volume_db_change != 0.0:
        clip = clip + volume_db_change
        
    # Áp dụng hiệu ứng mờ đầu và mờ cuối (Fading)
    if fade_in_ms > 0:
        clip = clip.fade_in(fade_in_ms)
    if fade_out_ms > 0:
        clip = clip.fade_out(fade_out_ms)
        
    # Tạo chuỗi lặp (Looping)
    looped_clip = clip * loop_count
    
    return looped_clip

def mix_multitrack_session(tracks_meta: list, output_path: str, sample_rate: int = 44100, bit_depth: int = 16):
    """
    Hòa âm đa kênh thống nhất (Multitrack Mixdown).
    
    Args:
        tracks_meta: Danh sách cấu hình của từng track:
                     [{"file_path": "...", "volume": 0.8, "muted": False}, ...]
        output_path: Đường dẫn file xuất
        sample_rate: Tần số lấy mẫu (Hz)
        bit_depth: Độ sâu bit (8, 16, hoặc 24)
        
    Returns:
        dict: Thông tin kết quả
    """
    master_mix = None
    
    for track in tracks_meta:
        if track.get("muted", False):
            continue
            
        # Đọc tệp âm thanh của track
        sound = AudioSegment.from_file(track["file_path"])
        
        # Áp dụng Gain (chuyển đổi từ tỷ lệ 0.0 -> 1.0 sang decibels dB)
        gain_raw = track.get("volume", 1.0)
        if gain_raw <= 0.001:
            continue  # Xem như tắt tiếng hoàn toàn
        gain_db = 20 * np.log10(gain_raw)
        sound = sound + gain_db
        
        # Gộp vào Master Mix
        if master_mix is None:
            master_mix = sound
        else:
            # overlay tự động đồng bộ thời điểm bắt đầu tại mốc 0ms
            master_mix = master_mix.overlay(sound, position=0)
            
    if master_mix is not None:
        # Đảm bảo thư mục output tồn tại
        out_dir = os.path.dirname(output_path)
        if out_dir:
            os.makedirs(out_dir, exist_ok=True)
        
        # Xác định format từ đuôi file
        output_ext = os.path.splitext(output_path)[1].lower().lstrip(".")
        
        if output_ext in ("mp3", "ogg"):
            # Xuất trực tiếp qua pydub/FFmpeg cho MP3, OGG
            master_mix.export(output_path, format=output_ext)
        else:
            # Cho WAV: sử dụng soundfile để kiểm soát bit-depth chính xác
            # Dùng tempfile an toàn thay vì đường dẫn tương đối
            with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
                temp_wav = tmp.name
            
            try:
                master_mix.export(temp_wav, format="wav")
                y, sr_read = sf.read(temp_wav)
                
                # Xác định subtype mã hóa bit-depth
                subtype_map = {8: "PCM_S8", 16: "PCM_16", 24: "PCM_24"}
                selected_subtype = subtype_map.get(bit_depth, "PCM_16")
                
                # Ghi tệp WAV chất lượng cao
                sf.write(output_path, y, sample_rate, subtype=selected_subtype)
            finally:
                # Dọn dẹp tệp tạm (luôn thực hiện)
                if os.path.exists(temp_wav):
                    os.remove(temp_wav)
            
        return {
            "success": True,
            "output_path": output_path,
            "duration_ms": len(master_mix),
            "format": output_ext if output_ext else "wav",
            "tracks_processed": len([t for t in tracks_meta if not t.get("muted", False)])
        }
    else:
        return {
            "success": False,
            "error": "No active tracks to mix"
        }


def export_audio(input_path: str, output_path: str, format: str = "wav",
                 sample_rate: int = 44100, bit_depth: int = 16):
    """
    Xuất tệp âm thanh sang nhiều định dạng (WAV/MP3/OGG).
    
    Args:
        input_path: Đường dẫn file nguồn
        output_path: Đường dẫn file đích
        format: Định dạng xuất (wav, mp3, ogg)
        sample_rate: Tần số lấy mẫu
        bit_depth: Độ sâu bit (chỉ cho WAV)
        
    Returns:
        dict: Thông tin kết quả
    """
    # Guard: prevent overwriting source file
    if os.path.abspath(input_path) == os.path.abspath(output_path):
        raise ValueError("output_path must not be the same as input_path")
    
    sound = AudioSegment.from_file(input_path)
    
    out_dir = os.path.dirname(output_path)
    if out_dir:
        os.makedirs(out_dir, exist_ok=True)
    
    if format in ("mp3", "ogg"):
        # Xuất qua pydub/FFmpeg
        sound.export(output_path, format=format)
    else:
        # WAV: dùng soundfile cho bit-depth chính xác
        with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
            temp_wav = tmp.name
        
        try:
            sound.export(temp_wav, format="wav")
            y, sr_read = sf.read(temp_wav)
            
            subtype_map = {8: "PCM_S8", 16: "PCM_16", 24: "PCM_24"}
            selected_subtype = subtype_map.get(bit_depth, "PCM_16")
            
            sf.write(output_path, y, sample_rate, subtype=selected_subtype)
        finally:
            if os.path.exists(temp_wav):
                os.remove(temp_wav)
    
    return {
        "success": True,
        "output_path": output_path,
        "format": format,
        "duration_ms": len(sound)
    }
