"""GE-Enforce enforcement-report per-entry key contract.

The server reads LOWERCASE keys from each results[] item -
name / action / selfhealed / exitcode / message (see
service.record_enforcement_report) - and maps them onto the
ManifestEnforcementResult columns entryname / action / selfhealed / exitcode /
message. This is the exact contract the PowerShell client fix targets: the
client emits lowercase keys, so if the server ever silently switched to reading
uppercase keys the client's entryname/selfhealed would drop to empty/false.

These tests POST a realistic multi-entry cycle with the lowercase keys and lock
that the result columns populate, plus the negative case (uppercase keys do NOT
populate) so a future contract flip fails loudly here.
"""

from plugins.geenforce import service


SCOPE = {
    'Version': '2.6',
    'Applications': [
        {'Name': 'VNC firewall rule', 'Type': 'PS1', 'Script': 'scripts/vnc.ps1'},
        {'Name': 'PC-DMIS 2023.1', 'Type': 'MSI', 'Installer': 'apps/pcdmis.msi'},
        {'Name': 'eDNC config', 'Type': 'PS1', 'Script': 'scripts/ednc.ps1'},
        {'Name': 'Blancco agent', 'Type': 'MSI', 'Installer': 'apps/blancco.msi'},
    ],
}


def _seed_and_publish(app, scopename='gea-shopfloor-cmm'):
    with app.app_context():
        service.replace_scope_draft(scopename, 'runtime', SCOPE)
        service.publish_scope(scopename, 'runtime', notes='v')
        service.db.session.commit()


def _report_token(client, auth_headers):
    resp = client.post('/api/apitokens',
                       json={'name': 'svc', 'scopes': ['geenforce.report']},
                       headers=auth_headers)
    assert resp.status_code == 201, resp.get_json()
    return resp.get_json()['data']['secret']


def test_lowercase_keys_populate_result_columns(client, db, app, auth_headers):
    _seed_and_publish(app)
    secret = _report_token(client, auth_headers)

    post = client.post('/api/geenforce/report', json={
        'hostname': 'WJDISPLAY07',
        'scopename': 'gea-shopfloor-cmm',
        'phase': 'runtime',
        'appliedversion': 1,
        'enforcerversion': '2.6',
        'counts': {'installed': 2, 'skipped': 1, 'failed': 1, 'filtered': 0},
        'results': [
            {'name': 'VNC firewall rule', 'action': 'installed',
             'selfhealed': True, 'exitcode': 0, 'message': 'rule re-added'},
            {'name': 'PC-DMIS 2023.1', 'action': 'skipped',
             'selfhealed': False, 'exitcode': 0, 'message': ''},
            {'name': 'eDNC config', 'action': 'installed',
             'selfhealed': True, 'exitcode': 0},
            {'name': 'Blancco agent', 'action': 'failed',
             'selfhealed': False, 'exitcode': 1603, 'message': 'MSI 1603'},
        ],
    }, headers={'X-API-Key': secret})
    assert post.status_code == 200, post.get_json()
    reportid = post.get_json()['data']['reportid']

    detail = client.get(f'/api/geenforce/reports/{reportid}',
                        headers=auth_headers).get_json()['data']
    byname = {r['entryname']: r for r in detail['results']}

    # Every lowercase 'name' mapped onto entryname (nothing dropped).
    assert set(byname) == {'VNC firewall rule', 'PC-DMIS 2023.1',
                           'eDNC config', 'Blancco agent'}

    vnc = byname['VNC firewall rule']
    assert vnc['action'] == 'installed'
    assert vnc['selfhealed'] is True
    assert vnc['exitcode'] == 0
    assert vnc['message'] == 'rule re-added'

    assert byname['PC-DMIS 2023.1']['action'] == 'skipped'
    assert byname['PC-DMIS 2023.1']['selfhealed'] is False

    ednc = byname['eDNC config']
    assert ednc['selfhealed'] is True
    assert ednc['message'] is None  # omitted key -> None

    blancco = byname['Blancco agent']
    assert blancco['action'] == 'failed'
    assert blancco['exitcode'] == 1603
    assert blancco['message'] == 'MSI 1603'

    # A failure is present, so the cycle status is 'failed' (a self-heal without
    # any failure would be 'selfhealed').
    assert detail['status'] == 'failed'


def test_selfhealed_defaults_false_when_key_omitted(client, db, app,
                                                    auth_headers):
    _seed_and_publish(app)
    secret = _report_token(client, auth_headers)
    post = client.post('/api/geenforce/report', json={
        'hostname': 'WJDISPLAY08',
        'scopename': 'gea-shopfloor-cmm',
        'results': [{'name': 'asset report', 'action': 'installed'}],
    }, headers={'X-API-Key': secret})
    reportid = post.get_json()['data']['reportid']

    detail = client.get(f'/api/geenforce/reports/{reportid}',
                        headers=auth_headers).get_json()['data']
    result = detail['results'][0]
    assert result['entryname'] == 'asset report'
    assert result['selfhealed'] is False


def test_uppercase_keys_do_not_populate(client, db, app, auth_headers):
    """Negative lock: the server reads LOWERCASE keys, so an uppercase-keyed
    item does NOT populate entryname/selfhealed. Guards against a silent flip to
    uppercase that would break the client's lowercase payload."""
    _seed_and_publish(app)
    secret = _report_token(client, auth_headers)
    post = client.post('/api/geenforce/report', json={
        'hostname': 'WJDISPLAY09',
        'scopename': 'gea-shopfloor-cmm',
        'results': [{'Name': 'wrong casing', 'Action': 'installed',
                     'SelfHealed': True}],
    }, headers={'X-API-Key': secret})
    reportid = post.get_json()['data']['reportid']

    detail = client.get(f'/api/geenforce/reports/{reportid}',
                        headers=auth_headers).get_json()['data']
    result = detail['results'][0]
    assert result['entryname'] == ''       # 'Name' not read -> default ''
    assert result['selfhealed'] is False   # 'SelfHealed' not read -> default
