"""CPU codec fixture only; no model/RTSP/Web or visual quality evaluation."""
import json
from pathlib import Path
import struct
import subprocess
import tempfile
import unittest


class MediaRuntimeTests(unittest.TestCase):
    def test_30fps_input_sparse_metadata_retains_pts(self):
        import gi
        gi.require_version('Gst','1.0')
        from gi.repository import Gst
        from app.event_capture import PACKET
        from app.artifact_media import render
        Gst.init(None)
        with tempfile.TemporaryDirectory() as directory:
            root=Path(directory)
            pipeline=Gst.parse_launch('videotestsrc num-buffers=61 ! video/x-raw,width=320,height=180,framerate=30/1 ! x264enc tune=zerolatency speed-preset=ultrafast key-int-max=30 bframes=0 ! h264parse ! video/x-h264,stream-format=byte-stream,alignment=au ! appsink name=sink sync=false')
            metadata=[];timestamps=[]
            try:
                pipeline.set_state(Gst.State.PLAYING)
                sink=pipeline.get_by_name('sink')
                with (root/'capture.bin').open('xb') as target:
                    while True:
                        sample=sink.emit('try-pull-sample',Gst.SECOND)
                        if sample is None:break
                        buffer=sample.get_buffer();payload=buffer.extract_dup(0,buffer.get_size())
                        caps=sample.get_caps().to_string().encode()
                        raw=PACKET.pack(buffer.pts,buffer.dts,buffer.duration,len(payload),len(caps),not buffer.has_flags(Gst.BufferFlags.DELTA_UNIT),0)+caps+payload
                        target.write(struct.pack('<I',len(raw)));target.write(raw)
                        if len(timestamps)%3==0:metadata.append(dict(ptsNs=buffer.pts,objects=[]))
                        timestamps.append(buffer.pts)
            finally:pipeline.set_state(Gst.State.NULL)
            event=dict(eventId='fixture',cameraId=1,ptsNs=metadata[10]['ptsNs'],aiRecordedAtWallMs=1789610000123)
            (root/'job.json').write_text(json.dumps(dict(event=event,metadata=metadata,startPtsNs=timestamps[0],
                endPtsNs=timestamps[-1],captureReason='FIXTURE',nativeDrop=0,metadataQueueDrop=0)))
            media=render(root)
            self.assertEqual(media['frames'],21)
            self.assertEqual(media['metadataMissingFrames'],40)
            self.assertTrue(media['thumbnail'])
            probe=json.loads(subprocess.check_output(['ffprobe','-v','error','-show_frames','-show_format','-of','json',str(root/'ready'/'clip.mp4')]))
            frames=probe['frames']
            self.assertEqual(len(frames),21)
            for index,frame in enumerate(frames):
                self.assertAlmostEqual(float(frame['pts_time']),index*.1,places=3)
            self.assertAlmostEqual(float(probe['format']['duration']),media['durationMs']/1000,delta=.005)


if __name__=='__main__':unittest.main()
