from copy import deepcopy
import json
from pathlib import Path
import sys

import pytest

sys.path.insert(0,str(Path(__file__).resolve().parents[1]))
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'tools/icerti_offline'))
from fight_r5_settings import migrate_spec, migrate_document
from app.fight_runtime import FightSettings
from app.secure_control import ControlService, Start
from app.stream_settings import patch_settings
from test_loitering_settings import FakeManager, spec, request


def test_absent_fight_stays_off_and_nonfight_exact():
    old=spec();old['fall']={'enabled':True};before=deepcopy(old)
    new=migrate_spec(old)
    assert old==before and not new['fight']['enabled']
    assert new['fight']['engine']=='r5'
    assert {k:v for k,v in new.items() if k!='fight'}==old


def test_explicit_legacy_migrates_preserving_higher_score_roi_and_cooldown():
    old=dict(spec(),fight={'engine':'legacy','enabled':True,'person_score':.15,'joint_score':.3,
                         'roi':[[.1,.1],[.8,.1],[.8,.8]],'roi_name':'existing'},event_cooldowns={'FIGHT':23})
    new=migrate_spec(old)
    assert new['fight']['person_score']==.25 and new['fight']['joint_score']==.3
    assert new['fight']['roi_name']=='existing' and new['event_cooldowns']==old['event_cooldowns']
    assert new==migrate_spec(new)


def test_documents_preserve_removed_and_empty_base():
    old={'added':[spec(),dict(spec(),stream_id='second')],'removed':['gone'],'custom':'preserve'}
    new=migrate_document(old,'runtime')
    assert new['removed']==old['removed'] and new['custom']=='preserve'
    assert all(s['fight']['engine']=='r5' for s in new['added'])
    assert migrate_document({'streams':[]},'base')=={'streams':[]}


def test_new_and_existing_web_start_keep_r5_and_old_values_noop():
    body=request('FIGHT');params=next(v for v in body['scenarios'] if v['scenarioCode']=='FIGHT')['parameters']
    params.update(arm_motion_ratio=99,required_bursts=1,person_confidence=.15,pose_confidence=.05)
    template=migrate_spec(spec());m=FakeManager();svc=ControlService(m,'server',template)
    result=svc.start_camera(Start(json.dumps(body)))
    assert result['fight']['engine']=='r5' and result['fight']['joint_score']==.1
    m=FakeManager(result);svc=ControlService(m,'server',template)
    repeated=svc.start_camera(Start(json.dumps(body)))
    assert repeated==result
    params.update(arm_motion_ratio=-10,required_bursts=999,person_confidence=.25,pose_confidence=.1)
    assert svc.start_camera(Start(json.dumps(body)))==result


def test_new_template_does_not_force_fight_on():
    m=FakeManager();result=ControlService(m,'server',migrate_spec(spec())).start_camera(Start(json.dumps(request())))
    assert result['fight']['engine']=='r5' and not result['fight']['enabled']


def test_reload_and_explicit_rollback():
    migrated=json.loads(json.dumps(migrate_spec(spec())))
    assert FightSettings.parse(migrated['fight']).engine=='r5'
    restored=patch_settings(migrated,{'fight':{'engine':'legacy'}})
    assert FightSettings.parse(restored['fight']).engine=='legacy'


@pytest.mark.parametrize('value',[{'fight':[]},{'fight':'bad'},{'fight':{'person_score':2}}])
def test_invalid_config_is_not_written(value):
    before=deepcopy(value)
    with pytest.raises(ValueError):migrate_spec(value)
    assert value==before


def test_protected_bindings_update_is_exact():
    from fight_r5_install import updated_bindings
    before={'/config/template.json':'old','/config/directory':{'directory':{'base.yaml':'old2'}},'/other':'keep'}
    changes=[dict(path='/config/template.json',changed=True,before='old',after='new'),
             dict(path='/config/directory/base.yaml',changed=True,before='old2',after='new2')]
    result=updated_bindings(before,changes)
    assert result['/config/template.json']=='new' and result['/other']=='keep'
    assert result['/config/directory']['directory']['base.yaml']=='new2'
    assert before['/config/template.json']=='old'
    changes[0]['before']='drift'
    with pytest.raises(ValueError):updated_bindings(before,changes)


def test_config_bind_uses_most_specific_mount():
    from fight_r5_install import host_path
    row={'Mounts':[dict(Type='bind',Source='/disk/config',Destination='/etc'),
                   dict(Type='bind',Source='/disk/template.json',Destination='/etc/template.json')]}
    assert str(host_path(row,'/etc/template.json'))=='/disk/template.json'
    with pytest.raises(ValueError):host_path(row,'/missing/config')
