"""Tests for the generic plugin collector contract (ADR-006).

Covers the auto-dispatched /api/collector/<plugin> endpoint, per-plugin API
key auth, idempotent upsert by identity field, and the JWT-protected
/api/collector/_schemas listing.
"""

import pytest

KEY = 'testcollectorkey'


@pytest.fixture
def collector_key(app):
    """Set the shared collector API key for the test."""
    old = app.config.get('COLLECTOR_API_KEY')
    app.config['COLLECTOR_API_KEY'] = KEY
    yield KEY
    app.config['COLLECTOR_API_KEY'] = old


@pytest.fixture
def computer_assettype(db):
    """Seed the computer asset type needed for create-on-ingest."""
    from shopdb.core.models import AssetType

    at = AssetType(assettype='computer', pluginname='computers',
                   tablename='computers', description='PCs')
    db.session.add(at)
    db.session.commit()
    return at


def test_schemas_requires_jwt(client, db):
    """The schema listing rejects unauthenticated callers."""
    response = client.get('/api/collector/_schemas')
    assert response.status_code == 401


def test_schemas_lists_computers(client, db, auth_headers):
    """Computers plugin exposes a collector schema keyed by hostname."""
    response = client.get('/api/collector/_schemas', headers=auth_headers)
    assert response.status_code == 200
    schemas = response.get_json()['data']['schemas']
    assert 'computers' in schemas
    assert schemas['computers']['identityfield'] == 'hostname'


def test_missing_key_rejected(client, db, collector_key, computer_assettype):
    """No API key -> 401."""
    response = client.post('/api/collector/computers',
                           json={'hostname': 'WJPC001'})
    assert response.status_code == 401


def test_unknown_plugin_404(client, db, collector_key):
    """A plugin with no collector schema returns 404."""
    response = client.post('/api/collector/nosuchplugin',
                           json={'hostname': 'x'},
                           headers={'X-API-Key': KEY})
    assert response.status_code == 404


def test_missing_identity_rejected(client, db, collector_key, computer_assettype):
    """Missing the identity field is a validation error."""
    response = client.post('/api/collector/computers',
                           json={'currentuser': 'someone'},
                           headers={'X-API-Key': KEY})
    assert response.status_code == 400


def test_create_then_idempotent_update(client, db, collector_key,
                                       computer_assettype):
    """First post creates; second post with same hostname updates (no dup)."""
    from plugins.computers.models import Computer

    payload = {'hostname': 'WJPC100', 'currentuser': 'alice',
               'serialnumber': 'SN-100'}

    first = client.post('/api/collector/computers', json=payload,
                        headers={'X-API-Key': KEY})
    assert first.status_code == 200, first.get_json()
    data = first.get_json()['data']
    assert data['action'] == 'created'
    assert data['assetid'] is not None
    assert data['identityvalue'] == 'WJPC100'

    payload['currentuser'] = 'bob'
    second = client.post('/api/collector/computers', json=payload,
                         headers={'X-API-Key': KEY})
    assert second.status_code == 200
    assert second.get_json()['data']['action'] == 'updated'

    with client.application.app_context():
        comps = Computer.query.filter(Computer.hostname.ilike('WJPC100')).all()
        assert len(comps) == 1
        assert comps[0].loggedinuser == 'bob'


def test_per_plugin_key_overrides_shared(client, db, app, computer_assettype):
    """COLLECTOR_API_KEY_<PLUGIN> takes precedence over the shared key."""
    app.config['COLLECTOR_API_KEY'] = 'sharedkey'
    app.config['COLLECTOR_API_KEY_COMPUTERS'] = 'computerskey'
    try:
        # Shared key now rejected for this plugin.
        rejected = client.post('/api/collector/computers',
                               json={'hostname': 'WJPC200'},
                               headers={'X-API-Key': 'sharedkey'})
        assert rejected.status_code == 401

        accepted = client.post('/api/collector/computers',
                               json={'hostname': 'WJPC200'},
                               headers={'X-API-Key': 'computerskey'})
        assert accepted.status_code == 200
    finally:
        app.config.pop('COLLECTOR_API_KEY_COMPUTERS', None)
        app.config['COLLECTOR_API_KEY'] = None


def test_complete_asset_payload_maps_enrollment_data(client, db, collector_key,
                                                     computer_assettype):
    """A GE-Enforce-shaped payload maps machinenumber/pctype/vendor/model/os."""
    from shopdb.core.models import OperatingSystem
    from plugins.computers.models import Computer, ComputerType

    # Seed the controlled-vocab rows the collector looks up.
    db.session.add(ComputerType(computertype='Shopfloor PC'))
    db.session.add(OperatingSystem(osname='Windows 11'))
    db.session.commit()

    payload = {
        'hostname': 'WJSF1234',
        'machinenumber': '0615',
        'pctype': 'gea-shopfloor-collections',
        'serialnumber': 'SN-ENROLL',
        'vendorname': 'Dell',
        'modelnumber': 'OptiPlex 7090',
        'osname': 'Windows 11',
    }
    resp = client.post('/api/collector/computers', json=payload,
                       headers={'X-API-Key': KEY})
    assert resp.status_code == 200, resp.get_json()
    assert resp.get_json()['data']['action'] == 'created'

    with client.application.app_context():
        comp = Computer.query.filter(Computer.hostname.ilike('WJSF1234')).first()
        assert comp is not None
        assert comp.asset.assetnumber == '0615'          # machinenumber -> assetnumber
        assert comp.asset.serialnumber == 'SN-ENROLL'
        assert comp.computertype.computertype == 'Shopfloor PC'  # pctype mapped
        assert comp.vendor.vendor == 'Dell'              # created
        assert comp.model.modelnumber == 'OptiPlex 7090'  # created
        assert comp.operatingsystem.osname == 'Windows 11'


def test_placeholder_machinenumber_9999_falls_back_to_hostname(client, db,
                                                               collector_key,
                                                               computer_assettype):
    """The 9999 imaging placeholder is ignored; assetnumber falls back to host."""
    resp = client.post('/api/collector/computers',
                       json={'hostname': 'WJSF9999', 'machinenumber': '9999'},
                       headers={'X-API-Key': KEY})
    assert resp.status_code == 200
    from plugins.computers.models import Computer
    with client.application.app_context():
        comp = Computer.query.filter(Computer.hostname.ilike('WJSF9999')).first()
        assert comp.asset.assetnumber == 'WJSF9999'
