import configparser
import ast
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from app.inference_profiles import render_cfg, inference_component_id
from app import inference_profiles


class MediumPreprocessTest(unittest.TestCase):
    def test_runtime_explicitly_passes_the_component_to_sdk(self):
        source = Path(inference_profiles.__file__).resolve().parent / 'multi_pipeline.py'
        calls = [node for node in ast.walk(ast.parse(source.read_text()))
                 if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)
                 and node.func.attr == 'preprocess']
        self.assertEqual(1, len(calls))
        self.assertTrue(any(k.arg == 'target_unique_ids' and isinstance(k.value, ast.Name)
                            and k.value.id == 'component_id' for k in calls[0].keywords))

    def setUp(self):
        templates = str(Path(inference_profiles.__file__).resolve().parent / 'cfg_templates')
        self.template_patch = patch('app.inference_profiles._CFG_DIR_CANDIDATES', (templates,))
        self.template_patch.start()
        self.addCleanup(self.template_patch.stop)

    def test_medium_tensor_meta_pair_and_cached_identity(self):
        with tempfile.TemporaryDirectory() as root, patch('app.inference_profiles._CFG_OUTPUT_DIR', root):
            path = Path(render_cfg('synthetic', 'det', 'RFDETRMedium'))
            self.assertIn('input_tensor_from_meta', path.read_text())
            self.assertIn('output_tensor_meta: false', path.read_text())
            self.assertIn('enable_cuda_buffer_sharing: true', path.read_text())
            self.assertIn('output_mem_type: MEMORY_TYPE_CPU', path.read_text())
            uid = inference_component_id('synthetic', 'det')
            self.assertIn(f'unique_id: {uid}', path.read_text())
            self.assertNotIn('normalize {', path.read_text())
            self.assertNotIn('{{', path.read_text())
            config = configparser.ConfigParser()
            config.read(str(path) + '.preprocess')
            props = config['property']
            self.assertEqual(str(uid), props['target-unique-ids'])
            self.assertEqual('1;3;576;576', props['network-input-shape'])
            self.assertEqual('1', props['scaling-pool-compute-hw'])
            self.assertEqual('2', props['scaling-pool-memory-type'])
            self.assertEqual('0', props['maintain-aspect-ratio'])
            self.assertEqual('4', props['scaling-buf-pool-size'])
            self.assertEqual('4', props['tensor-buf-pool-size'])
            self.assertEqual('0', props['network-color-format'])
            self.assertEqual('7', props['tensor-data-type'])
            self.assertEqual(str(path), render_cfg('synthetic', 'det', 'RFDETRMedium'))
            self.assertNotEqual(str(path), render_cfg('synthetic2', 'det', 'RFDETRMedium'))

    def test_other_det_keeps_native_preprocessing(self):
        with tempfile.TemporaryDirectory() as root, patch('app.inference_profiles._CFG_OUTPUT_DIR', root):
            path = Path(render_cfg('synthetic', 'det', 'RFDETRLarge'))
            self.assertIn('normalize {', path.read_text())
            self.assertNotIn('input_tensor_from_meta', path.read_text())
            self.assertFalse(Path(str(path)+'.preprocess').exists())
            self.assertIn('enable_cuda_buffer_sharing: false', path.read_text())

    def test_pose_and_fire_fp16_contract(self):
        with tempfile.TemporaryDirectory() as root, patch('app.inference_profiles._CFG_OUTPUT_DIR', root):
            for role, model, dtype, sharing in [('pose', 'RFDETRKeypointPreview', '7', 'true'),
                                               ('det', 'RFDETRFireSmoke', '7', 'true')]:
                path = Path(render_cfg('synthetic', role, model))
                config = configparser.ConfigParser()
                config.read(str(path)+'.preprocess')
                self.assertEqual(dtype, config['property']['tensor-data-type'])
                self.assertIn('enable_cuda_buffer_sharing: '+sharing, path.read_text())
                self.assertIn('output_mem_type: MEMORY_TYPE_CPU', path.read_text())
                if model == 'RFDETRFireSmoke':
                    self.assertIn('CreateContextraFireProcessor', path.read_text())
                self.assertNotIn('{{', path.read_text())
