"""Echtzeit-Parsing der ffmpeg stderr-Ausgabe"""
import re
import asyncio
import logging
from typing import Callable, Awaitable
from app.models.job import ConversionJob
from app.models.media import MediaFile


class ProgressParser:
    """
    Liest ffmpeg stderr und extrahiert Fortschrittsinformationen.
    Ruft Callback fuer WebSocket-Updates auf.
    Speichert letzte stderr-Zeilen fuer Fehlerdiagnose.
    """

    def __init__(self, on_progress: Callable[[ConversionJob], Awaitable[None]]):
        self.on_progress = on_progress
        self.last_lines: list[str] = []
        self._max_lines = 50

    async def monitor(self, job: ConversionJob) -> None:
        """
        Hauptschleife: Liest stderr des ffmpeg-Prozesses,
        parst Fortschritt und ruft Callback auf.
        """
        if not job.process or not job.process.stderr:
            return

        empty_reads = 0
        max_empty_reads = 30
        update_counter = 0

        while True:
            try:
                data = await job.process.stderr.read(1024)
            except Exception:
                break

            if not data:
                empty_reads += 1
                if empty_reads > max_empty_reads:
                    break
                await asyncio.sleep(0.5)
                continue

            empty_reads = 0
            line = data.decode(errors="replace")

            # Letzte Zeilen fuer Fehlerdiagnose speichern
            for part in line.splitlines():
                part = part.strip()
                if part:
                    self.last_lines.append(part)
                    if len(self.last_lines) > self._max_lines:
                        self.last_lines.pop(0)

            self._extract_values(job, line)
            self._calculate_progress(job)
            job.update_stats(job.progress_fps, job.progress_speed, job.progress_bitrate)

            # WebSocket-Update senden
            await self.on_progress(job)

            # Ausfuehrliches Logging alle 100 Reads
            update_counter += 1
            if update_counter % 100 == 0:
                logging.info(
                    f"[{job.media.source_filename}] "
                    f"{job.progress_percent:.1f}% | "
                    f"FPS: {job.progress_fps} | "
                    f"Speed: {job.progress_speed}x | "
                    f"ETA: {MediaFile.format_time(job.progress_eta_sec)}"
                )

    def get_error_output(self) -> str:
        """Gibt die letzten stderr-Zeilen als String zurueck"""
        return "\n".join(self.last_lines[-10:])

    @staticmethod
    def _extract_values(job: ConversionJob, line: str) -> None:
        """Regex-Extraktion aus ffmpeg stderr"""
        # Frame
        match = re.findall(r"frame=\s*(\d+)", line)
        if match:
            frames = int(match[-1])
            if frames > job.progress_frames:
                job.progress_frames = frames

        # FPS
        match = re.findall(r"fps=\s*(\d+\.?\d*)", line)
        if match:
            job.progress_fps = float(match[-1])

        # Speed
        match = re.findall(r"speed=\s*(\d+\.?\d*)", line)
        if match:
            job.progress_speed = float(match[-1])

        # Bitrate
        match = re.findall(r"bitrate=\s*(\d+)", line)
        if match:
            job.progress_bitrate = int(match[-1])

        # Size (KiB von ffmpeg)
        match = re.findall(r"size=\s*(\d+)", line)
        if match:
            size_kib = int(match[-1])
            size_bytes = size_kib * 1024
            if size_bytes > job.progress_size_bytes:
                job.progress_size_bytes = size_bytes

        # Time (HH:MM:SS)
        match = re.findall(r"time=\s*(\d+:\d+:\d+\.?\d*)", line)
        if match:
            seconds = MediaFile.time_to_seconds(match[-1])
            if seconds > job.progress_time_sec:
                job.progress_time_sec = seconds

    @staticmethod
    def _calculate_progress(job: ConversionJob) -> None:
        """Berechnet Fortschritt in % und ETA"""
        total_frames = job.media.total_frames
        if total_frames > 0:
            job.progress_percent = min(
                (job.progress_frames / total_frames) * 100, 100.0
            )

        # ETA basierend auf durchschnittlicher FPS
        if job.avg_fps > 0 and total_frames > 0:
            remaining_frames = total_frames - job.progress_frames
            job.progress_eta_sec = max(0, remaining_frames / job.avg_fps)
