import os
import time
from app.core.config import settings
from app.core.exceptions import AudioFileNotFoundError, ModelNotFoundError, STTError
from app.models.stt import STTRequest, STTResponse
from app.services.audio_parser import AudioParser
from app.services.speaker_classifier import classify_speaker
import logging

logger = logging.getLogger("uvicorn.error")

try:
    from faster_whisper import WhisperModel
except ImportError:
    WhisperModel = None



class WhisperSTTService:
    """
    faster-whisper를 이용하여 오디오를 텍스트로 변환하는 서비스 클래스.
    CPU / 내장그래픽 최적화를 위해 int8 양자화 모델을 기반으로 동작.
    """
    def __init__(self):
        self.model_name = settings.WHISPER_MODEL_NAME
        self._model = None

    def _load_model(self):
        """지연 로딩(Lazy Loading) 방식으로 모델을 초기화함."""
        if WhisperModel is None:
            raise ModelNotFoundError(
                "faster-whisper 모듈이 설치되어 있지 않습니다. "
                "'pip install faster-whisper'를 확인하세요."
            )
            
        if self._model is None:
            try:
                # CPU 타겟, int8 양자화로 모델 객체 생성. (최초 시도시 모델 자동 다운로드됨)
                self._model = WhisperModel(self.model_name, device="cpu", compute_type="int8")
            except Exception as e:
                raise ModelNotFoundError(f"Whisper 모델 로드 실패 ({self.model_name}): {str(e)}")

    def transcribe(self, request: STTRequest) -> STTResponse:
        """
        [Pydantic 모델 기반 입출력]
        오디오 파일을 STT 엔진에 전달하고 변환 결과 텍스트를 반환함.
        """
        if not os.path.exists(request.audio_file_path):
            raise AudioFileNotFoundError(request.audio_file_path)
            
        import tracemalloc
        tracemalloc.start()
        
        load_start = time.time()
        self._load_model()
        load_time = time.time() - load_start
        
        start_time = time.time()
        processed_wav = None
        
        try:
            # 1. 오디오 포맷 전처리 (m4a, mp3 -> 16kHz mono wav 변환)
            processed_wav = AudioParser.preprocess_audio(request.audio_file_path)
            
            # 오디오 길이(duration) 측정 
            # pydub를 사용하여 길이를 구하거나 wave 파일 헤더를 읽음 (간단히 pydub 사용)
            from pydub import AudioSegment
            audio_segment = AudioSegment.from_file(processed_wav)
            audio_duration_sec = len(audio_segment) / 1000.0
            
            # 사전 데이터 모듈을 통한 철도/지하철 용어 프롬프트 주입
            from app.core.dictionary import domain_dict
            stations_prompt = domain_dict.get_prompt()
            
            # 2. faster-whisper API 호출
            segments, info = self._model.transcribe(
                processed_wav, 
                beam_size=5,
                initial_prompt=stations_prompt,
                vad_filter=True, # VAD(단말기 감지)를 켜서 빈 구간 스킵 및 속도 향상
                word_timestamps=False,
                condition_on_previous_text=False # 무전 특성상 이전 맥락 환각 방지
            )
            
            # 3. generator를 순회하며 텍스트 추출 및 세그먼트 매핑
            from app.models.stt import STTSegment
            from datetime import timedelta
            
            stt_segments = []
            final_text_parts = []
            
            # 화자 분리 추적 변수
            current_speaker = "미상"
            prev_end_sec = 0.0
            
            for segment in segments:
                raw_seg_text = segment.text.strip()
                if not raw_seg_text:
                    continue
                
                # 개별 세그먼트에 후처리 필터 적용 (RapidFuzz 교정기)
                corrected_seg_text = domain_dict.post_process_correction(raw_seg_text, threshold=88.0).strip()
                
                # 빈 문자열로 교정된 경우 스킵
                if not corrected_seg_text:
                    continue
                    
                final_text_parts.append(corrected_seg_text)
                
                # 절대 시간 연산 (타임존 방어: naive datetime → KST 강제 적용)
                abs_start_time = None
                abs_end_time = None
                if request.base_datetime:
                    try:
                        from datetime import timezone
                        from zoneinfo import ZoneInfo
                        kst = ZoneInfo("Asia/Seoul")
                        
                        base = request.base_datetime
                        # naive datetime이면 KST로 강제 localize
                        if base.tzinfo is None:
                            base = base.replace(tzinfo=kst)
                        
                        abs_start_time = (base + timedelta(seconds=segment.start)).isoformat()
                        abs_end_time = (base + timedelta(seconds=segment.end)).isoformat()
                    except Exception:
                        pass  # 안전을 위해 예외시 None으로 둠
                        
                # ── 화자 분류: speaker_classifier (휴리스틱 → LLM JSON) ──
                context_chunk = " ".join(final_text_parts[-3:-1]) if len(final_text_parts) > 1 else ""
                current_speaker = classify_speaker(corrected_seg_text, context_chunk)
                logger.debug(f"[STT] 세그먼트 화자: '{corrected_seg_text[:30]}' → {current_speaker}")

                prev_end_sec = segment.end

                # [Chapter 8.0] 단일 세그먼트 Opus 압축 인코딩
                import subprocess
                import uuid
                os.makedirs("data/audio", exist_ok=True)
                audio_path = f"data/audio/seg_{int(time.time())}_{uuid.uuid4().hex[:6]}.ogg"
                try:
                    subprocess.run([
                        "ffmpeg", "-y", "-i", processed_wav,
                        "-ss", str(segment.start), "-to", str(segment.end),
                        "-c:a", "libopus", "-b:a", "16k", audio_path
                    ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
                except Exception as e:
                    logger.warning(f"ffmpeg Opus 인코딩 실패: {e}")
                    audio_path = None

                stt_segments.append(
                    STTSegment(
                        start_sec=round(segment.start, 2),
                        end_sec=round(segment.end, 2),
                        text=corrected_seg_text,
                        speaker=current_speaker,
                        absolute_start_time=abs_start_time,
                        absolute_end_time=abs_end_time,
                        audio_path=audio_path
                    )
                )
                
            processing_time = time.time() - start_time
            
            # 메모리 프로파일링 완료
            current_mem, peak_mem = tracemalloc.get_traced_memory()
            tracemalloc.stop()
            
            # 4. Pydantic Response 반환
            from app.services.analyzer import check_urgency, extract_train_number
            final_text = " ".join(final_text_parts)
            urgency = check_urgency(final_text)
            train_number = extract_train_number(final_text)
            
            return STTResponse(
                text=final_text,
                language=info.language if info else request.language,
                segments=stt_segments,
                processing_time_sec=round(processing_time, 2),
                load_time_sec=round(load_time, 2),
                audio_duration_sec=round(audio_duration_sec, 2),
                peak_memory_mb=round(peak_mem / 1024 / 1024, 2),
                process_speed_x=round(audio_duration_sec / processing_time, 2) if processing_time > 0 else 0,
                urgency=urgency,
                train_number=train_number
            )
            
        except STTError:
            raise
        except Exception as e:
            raise STTError(f"STT 변환 실패: {str(e)}")
        finally:
            if processed_wav and processed_wav != request.audio_file_path:
                AudioParser.cleanup(processed_wav)
