import os
import json
import numpy as np
from typing import Optional

# Thay librosa bang shim nhe (numpy/scipy/soundfile) — khong keo numba/llvmlite
from app.core.audio_features import (
    load as _load,
    beat_track as _beat_track,
    frames_to_time as _frames_to_time,
    spectral_centroid as _spectral_centroid,
    rms as _rms,
    zero_crossing_rate as _zcr,
)


def analyze_audio(file_path: str) -> dict:
    """
    Phân tích âm thanh: BPM, beat tracking, ước lượng bars.
    """
    # Load audio
    y, sr = _load(file_path, sr=None)
    
    # Track beats
    tempo, beat_frames = _beat_track(y=y, sr=sr)
    
    # Handle tempo which might be scalar or numpy array in different librosa versions
    if isinstance(tempo, np.ndarray):
        if tempo.size > 0:
            bpm = float(tempo[0])
        else:
            bpm = 120.0
    else:
        bpm = float(tempo)
        
    # Convert frames to time (seconds)
    beat_times = _frames_to_time(beat_frames, sr=sr).tolist()
    
    # Estimate bars (assume 4/4 time signature - grouping every 4 beats)
    bar_times = [beat_times[i] for i in range(0, len(beat_times), 4)]
    
    return {
        "bpm": round(bpm, 2),
        "beats": [round(t, 4) for t in beat_times],
        "bars": [round(t, 4) for t in bar_times],
        "duration": round(float(len(y)) / sr, 4)
    }


def analyze_audio_advanced(file_path: str) -> dict:
    """
    Phân tích âm thanh nâng cao: BPM, beats, bars, spectral features.
    Sử dụng librosa để trích xuất đặc trưng âm học chi tiết.
    """
    y, sr = _load(file_path, sr=None)
    duration = float(len(y)) / sr
    
    # Beat tracking
    tempo, beat_frames = _beat_track(y=y, sr=sr)
    
    if isinstance(tempo, np.ndarray):
        bpm = float(tempo[0]) if tempo.size > 0 else 120.0
    else:
        bpm = float(tempo)
        
    beat_times = _frames_to_time(beat_frames, sr=sr).tolist()
    bar_times = [beat_times[i] for i in range(0, len(beat_times), 4)]
    
    # Spectral centroid (brightness)
    spectral_centroids = _spectral_centroid(y=y, sr=sr)[0]
    avg_brightness = float(np.mean(spectral_centroids))
    
    # RMS energy
    rms_vals = _rms(y=y)[0]
    avg_energy = float(np.mean(rms_vals))
    
    # Zero crossing rate
    zcr_vals = _zcr(y)[0]
    avg_zcr = float(np.mean(zcr_vals))
    
    return {
        "bpm": round(bpm, 2),
        "beats": [round(t, 4) for t in beat_times],
        "bars": [round(t, 4) for t in bar_times],
        "duration": round(duration, 4),
        "spectral_centroid_avg": round(avg_brightness, 2),
        "rms_energy_avg": round(avg_energy, 6),
        "zero_crossing_rate_avg": round(avg_zcr, 6),
        "sample_rate": sr
    }


def analyze_structure_with_ai(
    file_path: str,
    api_base_url: Optional[str] = None,
    model: str = "deepseek-chat"
) -> dict:
    """
    Phân tích cấu trúc khuôn nhạc bằng AI (OpenAI Compatible API).
    Gọi API DeepSeek/Ollama để phân đoạn bố cục (Intro, Verse, Chorus, Outro).
    
    Args:
        file_path: Đường dẫn tệp âm thanh
        api_base_url: Base URL của API (mặc định dùng env OPENAI_API_BASE)
        model: Model name (DeepSeek, Ollama, etc.)
        
    Note:
        API key is read exclusively from OPENAI_API_KEY env var.
        
    Returns:
        dict: Kết quả phân tích cấu trúc
    """
    # Lấy thông tin phân tích cơ bản trước
    analysis = analyze_audio_advanced(file_path)
    
    # Cấu hình API - key chỉ đọc từ env var, không bao giờ truyền qua task queue
    base_url = api_base_url or os.getenv("OPENAI_API_BASE", "http://localhost:11434/v1")
    key = os.getenv("OPENAI_API_KEY", "ollama")
    
    # Chuẩn bị prompt phân tích
    prompt = f"""Analyze the following audio metadata and suggest the musical structure (sections).

Audio Analysis:
- BPM: {analysis['bpm']}
- Duration: {analysis['duration']} seconds
- Number of beats: {len(analysis['beats'])}
- Number of bars: {len(analysis['bars'])}
- Average spectral centroid: {analysis['spectral_centroid_avg']} Hz
- Average RMS energy: {analysis['rms_energy_avg']}
- Bar timestamps (seconds): {json.dumps(analysis['bars'][:20])}

Based on this data, estimate the song structure by identifying sections like Intro, Verse, Chorus, Bridge, Outro.
Respond ONLY with valid JSON in this exact format:
{{
  "sections": [
    {{"name": "Intro", "start_time": 0.0, "end_time": 8.5}},
    {{"name": "Verse", "start_time": 8.5, "end_time": 25.0}},
    {{"name": "Chorus", "start_time": 25.0, "end_time": 40.0}}
  ]
}}"""

    try:
        import httpx
        
        response = httpx.post(
            f"{base_url}/chat/completions",
            headers={
                "Authorization": f"Bearer {key}",
                "Content-Type": "application/json"
            },
            json={
                "model": model,
                "messages": [
                    {"role": "system", "content": "You are a music analysis AI. Respond only with valid JSON."},
                    {"role": "user", "content": prompt}
                ],
                "temperature": 0.3
            },
            timeout=30.0
        )
        
        if response.status_code == 200:
            result = response.json()
            ai_content = result["choices"][0]["message"]["content"]
            
            # Parse JSON response
            try:
                # Xử lý trường hợp AI trả về text bọc trong ```json ... ```
                if "```json" in ai_content:
                    ai_content = ai_content.split("```json")[1].split("```")[0]
                elif "```" in ai_content:
                    ai_content = ai_content.split("```")[1].split("```")[0]
                    
                sections = json.loads(ai_content.strip())
            except json.JSONDecodeError:
                sections = {"sections": [], "error": "AI response was not valid JSON"}
            
            return {
                **analysis,
                "ai_structure": sections,
                "ai_model": model,
                "ai_status": "success"
            }
        else:
            return {
                **analysis,
                "ai_structure": {"sections": []},
                "ai_model": model,
                "ai_status": f"API error: {response.status_code}"
            }
            
    except ImportError:
        # httpx not available, fall back to basic heuristic
        return {
            **analysis,
            "ai_structure": _estimate_structure_heuristic(analysis),
            "ai_model": "heuristic",
            "ai_status": "httpx not installed, using heuristic"
        }
    except Exception as e:
        # API không khả dụng, dùng heuristic
        return {
            **analysis,
            "ai_structure": _estimate_structure_heuristic(analysis),
            "ai_model": "heuristic",
            "ai_status": f"AI unavailable ({str(e)}), using heuristic"
        }


def _estimate_structure_heuristic(analysis: dict) -> dict:
    """
    Ước lượng cấu trúc bài hát bằng heuristic khi AI không khả dụng.
    Dựa trên số bars và duration để phân đoạn.
    """
    duration = analysis.get("duration", 0)
    bars = analysis.get("bars", [])
    
    if not bars or duration < 10:
        return {"sections": [{"name": "Full", "start_time": 0.0, "end_time": duration}]}
    
    sections = []
    num_bars = len(bars)
    
    if num_bars >= 8:
        # Intro: ~first 2 bars
        intro_end = bars[min(2, num_bars - 1)]
        sections.append({"name": "Intro", "start_time": 0.0, "end_time": round(intro_end, 4)})
        
        # Main body
        if num_bars >= 16:
            verse_end = bars[min(8, num_bars - 1)]
            sections.append({"name": "Verse", "start_time": round(intro_end, 4), "end_time": round(verse_end, 4)})
            
            if num_bars >= 24:
                chorus_end = bars[min(16, num_bars - 1)]
                sections.append({"name": "Chorus", "start_time": round(verse_end, 4), "end_time": round(chorus_end, 4)})
                
                # Outro: last bars to end
                outro_start = bars[min(num_bars - 2, num_bars - 1)]
                sections.append({"name": "Outro", "start_time": round(outro_start, 4), "end_time": round(duration, 4)})
            else:
                sections.append({"name": "Outro", "start_time": round(verse_end, 4), "end_time": round(duration, 4)})
        else:
            sections.append({"name": "Main", "start_time": round(intro_end, 4), "end_time": round(duration, 4)})
    else:
        sections.append({"name": "Full", "start_time": 0.0, "end_time": round(duration, 4)})
    
    return {"sections": sections}
