"""Every asset list's search box matches the optional identifiers (ADR-001).

Settings offers `gaugelabreference` and `maintenancereference` on machines, PCs,
printers, network devices and measuring tools. Both were shown on the form and
the detail page and neither was matched by any list's `?search=`, so someone
holding a physical tag could read it, type it into the list they were already
looking at, and get nothing - while global search found it.

One parametrized test over all five lists on purpose. The search clause is
copy-pasted five times, once per plugin, which is exactly how all five came to
omit a field their forms collect; a per-plugin test would drift the same way.
"""

import pytest

from shopdb.core.models import Asset, AssetType


# (list endpoint, create endpoint, assettype)
LISTS = [
    ('/api/machines', '/api/machines', 'machine'),
    ('/api/computers', '/api/computers', 'computer'),
    ('/api/printers', '/api/printers', 'printer'),
    ('/api/network', '/api/network', 'network_device'),
    ('/api/measuringtools', '/api/measuringtools', 'measuring_tool'),
]


def _seed(client, db, auth_headers, createpath, assettype, assetnumber):
    """Create one record of this type carrying both identifiers."""
    if not AssetType.query.filter_by(assettype=assettype).first():
        db.session.add(AssetType(assettype=assettype, pluginname=assettype,
                                 tablename=assettype, description=assettype))
        db.session.commit()

    resp = client.post(createpath, json={'assetnumber': assetnumber},
                       headers=auth_headers)
    assert resp.status_code == 201, (createpath, resp.get_json())

    asset = Asset.query.filter_by(assetnumber=assetnumber).first()
    asset.gaugelabreference = f'ZZG{assetnumber}'
    asset.maintenancereference = f'ZZM{assetnumber}'
    db.session.commit()
    return asset


def _numbers(client, auth_headers, listpath, query):
    """Asset numbers a list returns for a search term."""
    resp = client.get(f'{listpath}?search={query}', headers=auth_headers)
    assert resp.status_code == 200, (listpath, resp.get_json())
    found = []
    for row in resp.get_json()['data']:
        # Lists differ in shape; the asset number is the field all of them
        # carry, either directly or on a nested asset.
        found.append(row.get('assetnumber')
                     or (row.get('asset') or {}).get('assetnumber'))
    return [f for f in found if f]


@pytest.mark.parametrize('listpath,createpath,assettype', LISTS)
def test_a_list_search_finds_a_gauge_lab_reference(client, db, auth_headers,
                                                  listpath, createpath, assettype):
    assetnumber = f'ZZ{assettype[:4].upper()}01'
    _seed(client, db, auth_headers, createpath, assettype, assetnumber)
    assert assetnumber in _numbers(client, auth_headers, listpath,
                                   f'ZZG{assetnumber}')


@pytest.mark.parametrize('listpath,createpath,assettype', LISTS)
def test_a_list_search_finds_a_maintenance_reference(client, db, auth_headers,
                                                     listpath, createpath, assettype):
    assetnumber = f'ZZ{assettype[:4].upper()}02'
    _seed(client, db, auth_headers, createpath, assettype, assetnumber)
    assert assetnumber in _numbers(client, auth_headers, listpath,
                                   f'ZZM{assetnumber}')


@pytest.mark.parametrize('listpath,createpath,assettype', LISTS)
def test_a_list_search_still_excludes_a_non_match(client, db, auth_headers,
                                                 listpath, createpath, assettype):
    """Widening the clause must not turn the search box into a pass-through."""
    assetnumber = f'ZZ{assettype[:4].upper()}03'
    _seed(client, db, auth_headers, createpath, assettype, assetnumber)
    assert _numbers(client, auth_headers, listpath, 'ZZNOTHINGMATCHESTHIS') == []
