"""Tests for branding-logo upload and settings-secret masking.

Covers WP1 of the multi-site distribution work: the branding-logo upload
endpoint (admin-only, kind/extension validation, round-trip), the public
serve route, the secret-masking guarantee on the settings list, and the
canonical new-settings defaults.
"""

import io

from shopdb.core.api.settings import (
    build_default_settings,
    BRANDING_KIND_SETTINGS,
)


def _defaults_by_key():
    return {d['key']: d for d in build_default_settings()}


def test_anon_settings_list_excludes_nonpublic(client, db):
    """Anonymous GET /api/settings returns only the public allowlist - secret
    AND non-secret internal keys are absent, so plaintext can never leak."""
    from shopdb.core.models import Setting

    Setting.set('smtp_password', 'supersecret', valuetype='string', category='email')
    Setting.set('zabbix_token', 'tok-abc-123', valuetype='string', category='integrations')
    Setting.set('warranty_dell_clientsecret', 'shh', valuetype='string', category='integrations')
    # A public key stays visible for contrast.
    Setting.set('facility_name', 'Test Plant', valuetype='string', category='site')

    resp = client.get('/api/settings')
    assert resp.status_code == 200, resp.get_json()
    by_key = {s['key']: s['value'] for s in resp.get_json()['data']}

    # Non-public keys are not enumerable at all (stronger than masking).
    assert 'smtp_password' not in by_key
    assert 'zabbix_token' not in by_key
    assert 'warranty_dell_clientsecret' not in by_key
    # Plaintext secrets must not leak anywhere in the response body.
    assert 'supersecret' not in resp.get_data(as_text=True)
    assert 'tok-abc-123' not in resp.get_data(as_text=True)
    # A public key passes through untouched.
    assert by_key['facility_name'] == 'Test Plant'


def test_branding_upload_forbidden_for_non_admin(client, db, member_headers):
    """A role-less authenticated user cannot upload a branding logo."""
    data = {
        'kind': 'site',
        'file': (io.BytesIO(b'<svg/>'), 'logo.svg'),
    }
    resp = client.post('/api/settings/branding-logo', data=data,
                       content_type='multipart/form-data', headers=member_headers)
    assert resp.status_code == 403
    assert resp.get_json()['data']['error']['code'] == 'FORBIDDEN'


def test_branding_upload_requires_auth(client, db):
    """No token at all cannot reach the upload route."""
    data = {'kind': 'site', 'file': (io.BytesIO(b'<svg/>'), 'logo.svg')}
    resp = client.post('/api/settings/branding-logo', data=data,
                       content_type='multipart/form-data')
    assert resp.status_code in (401, 422)


def test_branding_upload_round_trip(client, db, auth_headers):
    """Admin upload writes the matching setting and the file is then served."""
    payload = b'<svg xmlns="http://www.w3.org/2000/svg"></svg>'
    data = {'kind': 'site', 'file': (io.BytesIO(payload), 'mylogo.svg')}
    resp = client.post('/api/settings/branding-logo', data=data,
                       content_type='multipart/form-data', headers=auth_headers)
    assert resp.status_code == 200, resp.get_json()
    body = resp.get_json()['data']
    assert body['key'] == 'site_logo'
    assert body['value'] == '/api/settings/branding/logo-site.svg'

    # Setting row now points at the served URL.
    from shopdb.core.models import Setting
    setting = Setting.query.filter_by(key='site_logo').first()
    assert setting is not None
    assert setting.value == '/api/settings/branding/logo-site.svg'

    # Public serve route returns the uploaded bytes without auth.
    served = client.get('/api/settings/branding/logo-site.svg')
    assert served.status_code == 200
    assert served.get_data() == payload


def test_branding_upload_favicon_allows_ico(client, db, auth_headers):
    """The favicon kind accepts .ico (extra extension beyond map images)."""
    data = {'kind': 'favicon', 'file': (io.BytesIO(b'icodata'), 'fav.ico')}
    resp = client.post('/api/settings/branding-logo', data=data,
                       content_type='multipart/form-data', headers=auth_headers)
    assert resp.status_code == 200, resp.get_json()
    assert resp.get_json()['data']['key'] == 'site_favicon'


def test_branding_upload_rejects_invalid_kind(client, db, auth_headers):
    """An unknown kind is a validation error."""
    data = {'kind': 'banner', 'file': (io.BytesIO(b'<svg/>'), 'logo.svg')}
    resp = client.post('/api/settings/branding-logo', data=data,
                       content_type='multipart/form-data', headers=auth_headers)
    assert resp.status_code == 400
    assert resp.get_json()['data']['error']['code'] == 'VALIDATION_ERROR'


def test_branding_upload_rejects_invalid_extension(client, db, auth_headers):
    """A disallowed file extension is rejected."""
    data = {'kind': 'site', 'file': (io.BytesIO(b'MZ...'), 'logo.exe')}
    resp = client.post('/api/settings/branding-logo', data=data,
                       content_type='multipart/form-data', headers=auth_headers)
    assert resp.status_code == 400
    assert resp.get_json()['data']['error']['code'] == 'VALIDATION_ERROR'


def test_kind_map_covers_expected_kinds():
    """The kind->setting map matches the documented branding kinds."""
    assert BRANDING_KIND_SETTINGS == {
        'site': 'site_logo',
        'qr': 'qr_logo',
        'badge': 'badge_logo',
        'favicon': 'site_favicon',
    }


def test_defaults_contain_new_branding_keys():
    """build_default_settings seeds every branding key with its default."""
    by_key = _defaults_by_key()
    expected = {
        'site_logo': '/ge-aerospace-logo.svg',
        'qr_logo': '/ge-monogram.svg',
        'badge_logo': '/ge-aerospace-logo.svg',
        'site_favicon': '',
        'brand_primary_color': '',
    }
    for key, value in expected.items():
        assert key in by_key, f'missing default {key}'
        assert by_key[key]['value'] == value
        assert by_key[key]['category'] == 'branding'


def test_defaults_contain_new_integration_keys():
    """build_default_settings seeds the ServiceNow integration keys."""
    by_key = _defaults_by_key()
    assert by_key['servicenow_enabled']['value'] == 'true'
    assert by_key['servicenow_enabled']['category'] == 'integrations'
    assert by_key['servicenow_ticket_prefixes']['value'] == 'GEINC,GECHG,GERIT,GESCT'
    assert '{ticket}' in by_key['servicenow_search_url']['value']
    assert '{ticket}' in by_key['servicenow_incident_url']['value']
    assert '{ticket}' in by_key['servicenow_change_url']['value']


def test_defaults_contain_new_site_keys():
    """build_default_settings seeds the employee-id and printer-hostname keys."""
    by_key = _defaults_by_key()
    assert by_key['employeeid_pattern']['value'] == r'^\d{9}$'
    assert by_key['printer_hostname_template']['value'] == 'Printer-{ip}.printer.geaerospace.net'
    assert by_key['contact_email_domain']['value'] == 'geaerospace.com'
    assert by_key['contact_email_domain']['category'] == 'site'


def test_defaults_contain_label_target_keys():
    """build_default_settings seeds the per-type label targets + label defaults."""
    by_key = _defaults_by_key()
    for key in ('qr_target_machine', 'qr_target_computer',
                'qr_target_network_device', 'qr_target_measuring_tool'):
        assert key in by_key, f'missing default {key}'
        assert by_key[key]['value'] == ''
        assert by_key[key]['category'] == 'printing'
    assert '{locationcode}' in by_key['qr_target_measuring_tool']['description']
    assert by_key['label_default_style']['value'] == 'card'
    assert by_key['label_default_codetype']['value'] == 'qr'
    assert by_key['label_default_style']['category'] == 'printing'
    assert by_key['label_default_codetype']['category'] == 'printing'
    # Per-type default encode mode: machines -> their machine number,
    # measuring tools -> inspection location, the rest -> asset page.
    assert by_key['label_default_encodes_machine']['value'] == 'assetnumber'
    assert by_key['label_default_encodes_measuring_tool']['value'] == 'location'
    for assettype in ('computer', 'printer', 'network_device'):
        assert by_key[f'label_default_encodes_{assettype}']['value'] == 'assetpage'
        assert by_key[f'label_default_encodes_{assettype}']['category'] == 'printing'


def test_defaults_changed_facility_and_map():
    """facility_name default is now blank; map blueprints point at the placeholder."""
    by_key = _defaults_by_key()
    assert by_key['facility_name']['value'] == ''
    assert by_key['map_blueprint_light']['value'] == '/static/images/floorplan-placeholder.svg'
    assert by_key['map_blueprint_dark']['value'] == '/static/images/floorplan-placeholder.svg'
    # No leftover West Jefferson sitemap references.
    assert 'sitemap2025' not in by_key['map_blueprint_light']['value']
