import os
import json
import unittest
from unittest.mock import patch
from deployment_kit.models import profile, pbtxt, trtexec_args
from app.fall_runtime import FallSettings, project_fall
from deployment_kit import release, site
from deployment_kit.core import KitError


class SiteIncrementalTests(unittest.TestCase):
    def test_crowd_resolution_contract(self):
        p = profile('crowd')
        self.assertEqual(p['resolution'], 512)
        self.assertEqual(p['outputs'], dict(dets=[300, 4], labels=[300, 3]))
        self.assertIn('dims: [3, 512, 512]', pbtxt(p))
        self.assertIn('--maxShapes=input:4x3x512x512', trtexec_args(p))
        self.assertNotIn(',\n]', pbtxt(p))

    def test_legacy_resolution_unchanged(self):
        for key in ('medium', 'pose', 'fire'):
            p = profile(key)
            self.assertEqual(p['resolution'], 576)
            self.assertIn('dims: [3, 576, 576]', pbtxt(p))

    def test_receipt_key_sort_preserves_config(self):
        for key in ('medium', 'pose', 'fire', 'crowd'):
            p = profile(key)
            self.assertEqual(pbtxt(p), pbtxt(json.loads(json.dumps(p, sort_keys=True))))

    def test_global_default_off(self):
        with patch.dict(os.environ, {}, clear=True):
            self.assertFalse(FallSettings.parse({}).policy.test_skip_down_hold)

    def test_site_default_and_explicit_off(self):
        with patch.dict(os.environ, CONTEXTRA_FALL_SKIP_DOWN_HOLD_DEFAULT='true'):
            self.assertTrue(FallSettings.parse({}).policy.test_skip_down_hold)
            self.assertFalse(FallSettings.parse({'test_skip_down_hold': False}).policy.test_skip_down_hold)

    def test_web_preserves_explicit_and_applies_absent(self):
        request = [dict(scenarioCode='FALL', enabled=True, confidence=.3, rois=[])]
        with patch.dict(os.environ, CONTEXTRA_FALL_SKIP_DOWN_HOLD_DEFAULT='true'):
            for original, expected in [({}, True), ({'fall': {'test_skip_down_hold': False}}, False)]:
                project_fall(original, request)
                self.assertEqual(original['fall']['test_skip_down_hold'], expected)
                project_fall(original, request)
                self.assertEqual(original['fall']['test_skip_down_hold'], expected)

    def test_invalid_site_default_rejected(self):
        with patch.dict(os.environ, CONTEXTRA_FALL_SKIP_DOWN_HOLD_DEFAULT='invalid'):
            with self.assertRaises(ValueError):
                FallSettings.parse({})

    def test_site_catalog_accepts_crowd(self):
        self.assertIn('Awiros_person_and_head_detection', site.MODELS)

    def test_legacy_deploy_and_rollback_require_patch_review(self):
        with patch.object(site, 'load', return_value={}), \
             patch.object(release, 'sha256', return_value='hash'), \
             patch.object(release.bundle, 'verify', return_value={}), \
             patch.object(release, 'active', return_value={'incremental_patch': 'receipt'}):
            for operation in (lambda: release.deploy('/unused', '/unused', 'hash'),
                              lambda: release.rollback('/unused', 'previous')):
                with self.assertRaises(KitError) as error:
                    operation()
                self.assertEqual(error.exception.code, 'INCREMENTAL_REVIEW_REQUIRED')
