"""Plugin frontend-contribution API endpoints (ADR-010).

Consumers for the four data-only presentation hooks on BasePlugin:
get_settings_cards, get_asset_panels, get_map_overlays, get_asset_presentation.

Every endpoint copies the dashboard-widgets consumer semantics: skip disabled
plugins, tag each contributed dict with its originating plugin name, fail loud
in dev/test, isolate a broken plugin in prod. All routes are GET + jwt-optional,
matching the other read-only core endpoints; they expose data, not mutations.
"""

from flask import Blueprint, request, current_app
from flask_jwt_extended import jwt_required

from shopdb.extensions import db
from shopdb.core.models import Asset
from shopdb.utils.responses import success_response, error_response, ErrorCodes

pluginui_bp = Blueprint('pluginui', __name__)


def _collect(hookname):
    """Aggregate a data-only hook across enabled plugins.

    Same access pattern as dashboard.get_widgets: skip disabled plugins, inject
    the plugin name on every returned dict, re-raise in dev/test, log-and-isolate
    a broken plugin in prod. Returns the merged list (unsorted).
    """
    pm = current_app.extensions.get('plugin_manager')
    if not pm:
        return []

    merged = []
    for name, plugin in pm.get_all_plugins().items():
        if not pm.registry.is_enabled(name):
            continue
        try:
            for entry in getattr(plugin, hookname)() or []:
                entry['plugin'] = name
                merged.append(entry)
        except Exception:
            # fail loud in dev/test, isolate broke plugin in prod
            if current_app.config.get('DEBUG') or current_app.config.get('TESTING'):
                raise
            current_app.logger.exception('Plugin %s %s failed', name, hookname)
    return merged


@pluginui_bp.route('/settings-cards', methods=['GET'])
@jwt_required(optional=True)
def settings_cards():
    """Merge enabled plugins' settings-catalog cards (get_settings_cards hook).

    The frontend merges these into the core settingsNav catalog and renders
    them in the settings rail + landing overview.
    """
    cards = _collect('get_settings_cards')
    cards.sort(key=lambda card: card.get('position', 99))
    return success_response(cards)


@pluginui_bp.route('/asset-panels', methods=['GET'])
@jwt_required(optional=True)
def asset_panels():
    """Return the asset-detail panels matching one asset's type (get_asset_panels).

    Query param assetid is required. A panel matches when its assettypes list
    contains the asset's type key or the wildcard '*'.
    """
    assetid = request.args.get('assetid', type=int)
    if not assetid:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'assetid is required')

    asset = db.session.get(Asset, assetid)
    if not asset:
        return error_response(ErrorCodes.NOT_FOUND, 'Asset not found', http_code=404)
    assettype = asset.assettype.assettype if asset.assettype else None

    matched = []
    for panel in _collect('get_asset_panels'):
        assettypes = panel.get('assettypes') or []
        if '*' in assettypes or (assettype and assettype in assettypes):
            matched.append(panel)
    matched.sort(key=lambda panel: panel.get('position', 99))
    return success_response(matched)


@pluginui_bp.route('/map-overlays', methods=['GET'])
@jwt_required(optional=True)
def map_overlays():
    """Merge enabled plugins' map overlay declarations (get_map_overlays hook).

    The map fetches each overlay endpoint and decorates already-placed markers.
    """
    overlays = _collect('get_map_overlays')
    overlays.sort(key=lambda overlay: overlay.get('position', 99))
    return success_response(overlays)


@pluginui_bp.route('/asset-presentation', methods=['GET'])
@jwt_required(optional=True)
def asset_presentation():
    """Merge enabled plugins' asset-type presentation entries (hook).

    Search rows and cross-links use these to pick a type's icon and detail route.
    """
    entries = _collect('get_asset_presentation')
    return success_response(entries)
