import uuid
from pathlib import Path
from typing import Optional

from .audio_metrics import analyze_audio
from .models import AssessmentResult, SentenceBoundaryDocument, TextSubstitution
from .repository import VideoRepository
from .scoring import SCORING_VERSION, score_attempt
from .transcription import Transcriber


class AssessmentService:
    def __init__(
        self,
        repository: VideoRepository,
        transcriber: Transcriber,
        pass_score: float,
    ):
        self.repository = repository
        self.transcriber = transcriber
        self.pass_score = pass_score

    def assess(
        self,
        *,
        document: SentenceBoundaryDocument,
        sentence_index: int,
        audio_path: Path,
        language: Optional[str] = None,
        retained_audio_filename: Optional[str] = None,
        user_id: str = "",
    ) -> AssessmentResult:
        if not self.transcriber.available:
            raise RuntimeError("MOSS transcription is not configured on this server.")
        sentence = next(
            (item for item in document.sentences if item.index == sentence_index),
            None,
        )
        if sentence is None:
            raise LookupError("Sentence index was not found.")
        if not sentence.text or not sentence.text.strip():
            raise ValueError("The sentence has no reviewed reference text yet.")

        student_metrics = analyze_audio(audio_path)
        transcript = self.transcriber.transcribe(
            audio_path,
            language or sentence.language,
        )
        recognized_text = transcript.text.strip()
        if not recognized_text:
            raise ValueError("MOSS did not recognize any speech in the recording.")

        reference_duration_ms = sentence.end_ms - sentence.start_ms
        reference_speech_duration_ms = (
            sentence.reference_speech_duration_ms or reference_duration_ms
        )
        score = score_attempt(
            reference_text=sentence.text,
            recognized_text=recognized_text,
            reference_speech_duration_ms=reference_speech_duration_ms,
            student_metrics=student_metrics,
        )
        attempt_id = uuid.uuid4().hex
        result = AssessmentResult(
            attempt_id=attempt_id,
            scoring_version=SCORING_VERSION,
            overall_score=score.overall_score,
            passed=score.overall_score >= self.pass_score,
            pass_score=self.pass_score,
            content_score=score.content_score,
            completeness_score=score.completeness_score,
            fluency_score=score.fluency_score,
            pronunciation_score=None,
            prosody_score=None,
            duration_score=score.duration_score,
            pause_score=score.pause_score,
            speech_rate_score=score.speech_rate_score,
            reference_text=sentence.text,
            recognized_text=recognized_text,
            reference_duration_ms=reference_duration_ms,
            reference_speech_duration_ms=reference_speech_duration_ms,
            student_recording_duration_ms=student_metrics.recording_duration_ms,
            student_speech_duration_ms=student_metrics.speech_duration_ms,
            duration_ratio=score.duration_ratio,
            missing_tokens=score.missing_tokens,
            extra_tokens=score.extra_tokens,
            substitutions=[
                TextSubstitution(expected=expected, actual=actual)
                for expected, actual in score.substitutions
            ],
            feedback=score.feedback,
            details={
                "score_kind": "朗读内容与流畅度匹配分",
                "content_weight": "0.80",
                "fluency_weight": "0.20",
                "duration_weight_within_fluency": "0.35",
                "phoneme_scoring": "not_enabled",
            },
        )
        if self.repository.get_video(document.video_hash) is not None:
            self.repository.record_attempt(
                attempt_id=attempt_id,
                video_hash=document.video_hash,
                sentence_index=sentence_index,
                result=result.model_dump(mode="json"),
                audio_filename=retained_audio_filename,
                user_id=user_id,
            )
        return result
