import json
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import patch

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'tools'))
import rtsp_test_prepare as prep


class PrepareTest(unittest.TestCase):
    def setUp(self):
        self.temp = tempfile.TemporaryDirectory()
        self.addCleanup(self.temp.cleanup)
        self.root = Path(self.temp.name)
        self.source = self.root / 'input #1.mp4'
        self.source.write_bytes(b'original')
        self.info = dict(path=str(self.source), width=1280, height=720, duration='3')

    def test_odd_dimensions_do_not_launch(self):
        with patch.object(prep, 'video_info', return_value=dict(self.info, width=1279)), patch.object(prep.subprocess, 'Popen') as launch:
            with self.assertRaises(prep.SenderError):
                prep.prepare_h264(self.source, self.root / 'out')
            launch.assert_not_called()
        self.assertEqual(self.source.read_bytes(), b'original')

    def test_disk_floor_does_not_launch(self):
        with patch.object(prep, 'video_info', return_value=self.info), patch.object(prep.shutil, 'disk_usage') as disk, patch.object(prep.subprocess, 'Popen') as launch:
            disk.return_value.free = 0
            with self.assertRaises(prep.SenderError):
                prep.prepare_h264(self.source, self.root / 'out')
            launch.assert_not_called()

    def test_failure_preserves_source_and_no_published_output(self):
        with patch.object(prep, 'video_info', return_value=self.info), patch.object(prep.shutil, 'disk_usage') as disk, patch.object(prep.subprocess, 'Popen') as launch:
            disk.return_value.free = 20 * 1024**3
            launch.return_value.poll.return_value = 1
            launch.return_value.returncode = 1
            with self.assertRaises(prep.SenderError):
                prep.prepare_h264(self.source, self.root / 'out')
        self.assertFalse(list((self.root / 'out').glob('*.mp4')))
        self.assertEqual(self.source.read_bytes(), b'original')

    def test_success_and_reuse(self):
        def launch(args, **kwargs):
            Path(args[-1]).write_bytes(b'prepared')
            from unittest.mock import Mock
            p = Mock()
            p.poll.return_value = p.returncode = 0
            return p
        def info(path, base=None):
            return self.info if Path(path) == self.source else dict(self.info, codec_name='h264')
        with patch.object(prep, 'video_info', side_effect=info), patch.object(prep.shutil, 'disk_usage') as disk, patch.object(prep.subprocess, 'Popen', side_effect=launch) as run, patch.object(prep, 'command', return_value='ffmpeg fixture'):
            disk.return_value.free = 20 * 1024**3
            first = prep.prepare_h264(self.source, self.root / 'out')
            second = prep.prepare_h264(self.source, self.root / 'out')
            self.assertFalse(first['reused'])
            self.assertTrue(second['reused'])
            self.assertEqual(run.call_count, 1)
            Path(first['output']).write_bytes(b'changed')
            with self.assertRaises(prep.SenderError):
                prep.prepare_h264(self.source, self.root / 'out')


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