"""GE-Enforce inline payloads: upload small scripts/configs into shopdb.

An entry can carry one inline payload (<= 1 MB) stored as ManifestPayload bytes.
Upload computes a sha256, sets the entry's payloadsource='inline' + payloadref,
and enforces one payload row per entry (re-upload replaces). GET returns the
raw bytes. Upload needs geenforce.publish; GET needs geenforce.manage. appid and
the payload metadata are shopdb-only and never enter the served manifest JSON.
"""

import hashlib
import io

from plugins.geenforce.models import ManifestPayload


def _create_scope(client, auth_headers, name='gea-shopfloor-cmm'):
    resp = client.post('/api/geenforce/scopes',
                       json={'scopename': name, 'phase': 'runtime'},
                       headers=auth_headers)
    assert resp.status_code == 201, resp.get_json()
    return resp.get_json()['data']['scopeid']


def _add_entry(client, auth_headers, scopeid, name='eDNC install'):
    resp = client.post(f'/api/geenforce/scopes/{scopeid}/entries',
                       json={'Name': name, 'Type': 'PS1',
                             'Script': 'scripts/x.ps1'},
                       headers=auth_headers)
    assert resp.status_code == 201, resp.get_json()
    return resp.get_json()['data']['entryid']


def _upload(client, headers, entryid, content, filename='config.reg',
            content_type='multipart/form-data'):
    return client.post(
        f'/api/geenforce/entries/{entryid}/payload',
        data={'file': (io.BytesIO(content), filename)},
        content_type=content_type, headers=headers)


def test_upload_stores_payload_and_links_entry(client, db, auth_headers):
    scopeid = _create_scope(client, auth_headers)
    entryid = _add_entry(client, auth_headers, scopeid)
    content = b'Windows Registry Editor Version 5.00\r\n'

    resp = _upload(client, auth_headers, entryid, content)
    assert resp.status_code == 201, resp.get_json()
    data = resp.get_json()['data']
    assert data['payloadsource'] == 'inline'
    assert data['payloadref'] == 'config.reg'
    assert data['payloadsha256'] == hashlib.sha256(content).hexdigest()
    assert data['haspayload'] is True

    rows = ManifestPayload.query.filter_by(entryid=entryid).all()
    assert len(rows) == 1
    assert rows[0].payloadbytes == content


def test_reupload_replaces_single_row(client, db, auth_headers):
    scopeid = _create_scope(client, auth_headers)
    entryid = _add_entry(client, auth_headers, scopeid)
    _upload(client, auth_headers, entryid, b'first')
    newcontent = b'second version'
    resp = _upload(client, auth_headers, entryid, newcontent)
    assert resp.status_code == 201

    rows = ManifestPayload.query.filter_by(entryid=entryid).all()
    assert len(rows) == 1
    assert rows[0].payloadbytes == newcontent
    assert rows[0].payloadsha256 == hashlib.sha256(newcontent).hexdigest()


def test_oversized_payload_rejected(client, db, auth_headers):
    scopeid = _create_scope(client, auth_headers)
    entryid = _add_entry(client, auth_headers, scopeid)
    toobig = b'x' * (1024 * 1024 + 1)
    resp = _upload(client, auth_headers, entryid, toobig)
    assert resp.status_code == 400
    assert ManifestPayload.query.filter_by(entryid=entryid).count() == 0


def test_empty_and_missing_file_rejected(client, db, auth_headers):
    scopeid = _create_scope(client, auth_headers)
    entryid = _add_entry(client, auth_headers, scopeid)
    empty = _upload(client, auth_headers, entryid, b'')
    assert empty.status_code == 400
    nofile = client.post(f'/api/geenforce/entries/{entryid}/payload',
                         data={}, content_type='multipart/form-data',
                         headers=auth_headers)
    assert nofile.status_code == 400


def test_download_returns_exact_bytes(client, db, auth_headers):
    scopeid = _create_scope(client, auth_headers)
    entryid = _add_entry(client, auth_headers, scopeid)
    content = b'\x00\x01binary payload\xff'
    _upload(client, auth_headers, entryid, content, filename='blob.bin')

    resp = client.get(f'/api/geenforce/entries/{entryid}/payload',
                      headers=auth_headers)
    assert resp.status_code == 200
    assert resp.data == content
    assert 'blob.bin' in resp.headers['Content-Disposition']


def test_download_404_when_no_payload(client, db, auth_headers):
    scopeid = _create_scope(client, auth_headers)
    entryid = _add_entry(client, auth_headers, scopeid)
    resp = client.get(f'/api/geenforce/entries/{entryid}/payload',
                      headers=auth_headers)
    assert resp.status_code == 404


def test_upload_404_unknown_entry(client, db, auth_headers):
    resp = _upload(client, auth_headers, 999999, b'data')
    assert resp.status_code == 404


def test_upload_requires_publish_permission(client, db, auth_headers,
                                            member_headers):
    scopeid = _create_scope(client, auth_headers)
    entryid = _add_entry(client, auth_headers, scopeid)
    # member (no permissions) cannot upload.
    resp = _upload(client, member_headers, entryid, b'data')
    assert resp.status_code == 403
