import json
import unittest
from copy import deepcopy

from app.crowd_config import CROWD_MODEL, FULL_FRAME
from app.crowd_runtime import project_crowd_candidate, patch_crowd_candidate, crowd_plan
from app.provider_plan import plan
from app.secure_control import validate_start


def scenarios(enabled=('CROWD',), **crowd_changes):
    rows = [dict(scenarioCode=k, enabled=k in enabled, rois=[], parameters={})
            for k in ('FIRE','FALL','INTRUSION','LOITERING','FIGHT','CROWD')]
    rows[-1].update(crowd_changes)
    return rows


def spec():
    return dict(server_id='server', source_id='1', tracking=dict(enabled=True),
                store=False, active_learning=False)


def request(enabled=('CROWD',)):
    return dict(cameraId=1, rtspUrl='rtsp://example.invalid/live',
                startWallTimeMs=1, rtpClockRate=90000, scenarios=scenarios(enabled))


class AdmissionTest(unittest.TestCase):
    def test_web_start_allows_crowd_coexistence(self):
        validate_start(request(('CROWD','FIRE','FALL','FIGHT','INTRUSION','LOITERING')))

    def test_local_plan_keeps_crowd_independent(self):
        value = spec()
        value['crowd'] = dict(enabled=True)
        p = plan(value)
        self.assertEqual([x.key for x in p.providers], ['crowd'])
        self.assertEqual(p.providers[0].model, CROWD_MODEL)

    def test_crowd_does_not_take_global_tracking_or_profile_ownership(self):
        value = spec()
        value['inference_profile'] = {'det_model':'RFDETRMedium'}
        before = deepcopy(value)
        projected = project_crowd_candidate(value, scenarios())
        self.assertEqual(value, before)
        self.assertTrue(projected['crowd']['enabled'])
        self.assertTrue(projected['tracking']['enabled'])
        self.assertEqual(projected['inference_profile'], before['inference_profile'])

    def test_parameters_remain_class_specific(self):
        value = spec()
        value['crowd'] = dict(person_confidence=.7, head_confidence=.6, window_seconds=5)
        projected = project_crowd_candidate(value, scenarios())['crowd']
        self.assertEqual((projected['person_confidence'], projected['head_confidence'],
                          projected['window_seconds']), (.7,.6,5))

    def test_first_roi_only_and_empty_full_frame(self):
        rois = [
            dict(label='first', points=[[0,0],[.5,0],[.5,1],[0,1]]),
            dict(label='second', points=[[.5,0],[1,0],[1,1],[.5,1]])]
        projected = project_crowd_candidate(spec(), scenarios(rois=rois))['crowd']
        self.assertEqual(projected['roi'], rois[0]['points'])
        self.assertEqual(projected['roi_name'], 'first')
        self.assertEqual(project_crowd_candidate(spec(), scenarios())['crowd']['roi'],
                         [list(x) for x in FULL_FRAME])

    def test_disabled_absent_projection_no_change(self):
        value = spec()
        self.assertEqual(project_crowd_candidate(value, scenarios(())), value)

    def test_patch_is_pure(self):
        value = spec()
        self.assertEqual(patch_crowd_candidate(value, {}), value)
        changed = patch_crowd_candidate(value, {'window_seconds':5})
        self.assertNotIn('crowd', value)
        self.assertEqual(changed['crowd']['window_seconds'], 5)

    def test_unknown_local_config_still_rejected(self):
        value = spec()
        value['crowd'] = dict(enabled=True, verified=True)
        with self.assertRaises(ValueError):
            crowd_plan(value)


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