"""Dashboard API endpoints."""

from flask import Blueprint, current_app
from flask_jwt_extended import jwt_required

from shopdb.extensions import db
from shopdb.core.models import Asset, AssetType, AssetStatus
from shopdb.utils.responses import success_response

dashboard_bp = Blueprint('dashboard', __name__)

# Map asset type name -> dashboard category label
_TYPE_CATEGORY = {
    'machine': 'Machine',
    'computer': 'PC',
    'printer': 'Printer',
    'network_device': 'Network',
    'measuring_tool': 'Measuring Tool',
}


def _count_by_type(assettype):
    query = db.session.query(Asset).join(AssetType).filter(
        Asset.isactive == True,
        AssetType.assettype == assettype
    )
    # Dualpath single-machine collapse: subtract the hidden secondary bays so a
    # dual-bay machine counts once. Only machines are ever Dualpath-paired, but
    # the AssetType filter above keeps this correct for any type. Default on.
    if assettype == 'machine':
        from shopdb.core.services.dualpath import (
            resolve_dualpath_pairs, dualpath_single_machine_enabled)
        if dualpath_single_machine_enabled():
            secondaryassetids = resolve_dualpath_pairs().secondaryassetids
            if secondaryassetids:
                query = query.filter(Asset.assetid.notin_(secondaryassetids))
    return query.count()


COUNTEDTYPES = ('machine', 'computer', 'network_device', 'printer',
                'measuring_tool')


def _countedassets():
    """Base query for the assets the dashboard totals describe.

    The status tiles and the type tiles MUST count the same population. They
    did not: the totals summed five specific types and subtracted dual-bay
    secondaries, while the status counts took every asset row of any type with
    no collapse. So "all assets 704" sat beside "in use 737", and both were
    correct about different things - which makes them worse than either alone.
    """
    from shopdb.core.services.dualpath import (
        resolve_dualpath_pairs, dualpath_single_machine_enabled)

    query = (db.session.query(Asset).join(AssetType)
             .filter(Asset.isactive == True,
                     AssetType.assettype.in_(COUNTEDTYPES)))
    if dualpath_single_machine_enabled():
        secondaryassetids = resolve_dualpath_pairs().secondaryassetids
        if secondaryassetids:
            query = query.filter(Asset.assetid.notin_(secondaryassetids))
    return query


@dashboard_bp.route('/summary', methods=['GET'])
@dashboard_bp.route('', methods=['GET'])
@jwt_required(optional=True)
def get_dashboard():
    """Get dashboard summary data (asset-based)."""
    machine_count = _count_by_type('machine')
    pc_count = _count_by_type('computer')
    network_count = _count_by_type('network_device')
    printer_count = _count_by_type('printer')
    measuringtool_count = _count_by_type('measuring_tool')
    total = machine_count + pc_count + network_count + printer_count + measuringtool_count

    # Count by status over the SAME population as the totals above, so the
    # tiles can be read against each other.
    status_dict = {}
    for asset in _countedassets().all():
        name = asset.status.status if asset.status else 'Unknown'
        status_dict[name] = status_dict.get(name, 0) + 1

    # Recent assets
    recent = Asset.query.filter_by(isactive=True).order_by(
        Asset.createddate.desc()
    ).limit(10).all()

    return success_response({
        # Fields expected by frontend (totalmachines now means machines,
        # totalassets is the grand total - renamed with the machines plugin)
        'totalassets': total,
        'totalmachines': machine_count,
        'totalpc': pc_count,
        'totalnetwork': network_count,
        'totalprinter': printer_count,
        'totalmeasuringtool': measuringtool_count,
        'activeassets': status_dict.get('In Use', 0),
        'inrepair': status_dict.get('In Repair', 0),
        # Structured data
        'counts': {
            'machines': machine_count,
            'pcs': pc_count,
            'networkdevices': network_count,
            'printers': printer_count,
            'measuringtools': measuringtool_count,
            'total': total
        },
        'bystatus': status_dict,
        'recent': [
            {
                'assetid': a.assetid,
                'assetnumber': a.assetnumber,
                'assettype': a.assettype.assettype if a.assettype else None,
                'createddate': a.createddate.isoformat() + 'Z' if a.createddate else None
            }
            for a in recent
        ]
    })


@dashboard_bp.route('/stats', methods=['GET'])
@jwt_required(optional=True)
def get_stats():
    """Get detailed statistics by asset type."""
    type_counts = db.session.query(
        AssetType.assettype,
        db.func.count(Asset.assetid)
    ).outerjoin(
        Asset,
        db.and_(Asset.assettypeid == AssetType.assettypeid, Asset.isactive == True)
    ).group_by(AssetType.assettypeid).all()

    return success_response({
        'bytype': [
            {
                'type': t,
                'category': _TYPE_CATEGORY.get(t, t),
                'count': count
            }
            for t, count in type_counts
        ]
    })


@dashboard_bp.route('/navigation', methods=['GET'])
def get_navigation():
    """Get navigation items from all loaded plugins."""
    pm = current_app.extensions.get('plugin_manager')
    if not pm:
        return success_response([])

    all_items = []

    # Core navigation items (always present)
    all_items.extend([
        {'name': 'Dashboard', 'icon': 'layout-dashboard', 'route': '/', 'position': 0},
        {'name': 'Map', 'icon': 'map', 'route': '/map', 'position': 4},
    ])

    # Collect navigation items from enabled plugins. Disabling persists to the
    # registry immediately, so a disabled plugin drops out of the menu right
    # away (its routes stay registered until the next restart - Flask cannot
    # unregister a blueprint at runtime).
    for name, plugin in pm.get_all_plugins().items():
        if not pm.registry.is_enabled(name):
            continue
        try:
            items = plugin.get_navigation_items()
            for item in items:
                item['plugin'] = name
            all_items.extend(items)
        except Exception:
            # Fail loud in dev/test; isolate a broken plugin in prod.
            if current_app.config.get('DEBUG') or current_app.config.get('TESTING'):
                raise
            current_app.logger.exception(
                'Plugin %s get_navigation_items failed', name)

    # Add core information section items
    all_items.extend([
        {'name': 'Applications', 'icon': 'app-window', 'route': '/applications', 'position': 30, 'section': 'information'},
        {'name': 'Reports', 'icon': 'bar-chart-3', 'route': '/reports', 'position': 40, 'section': 'information'},
    ])

    # Sort by position
    all_items.sort(key=lambda x: x.get('position', 99))

    return success_response(all_items)


@dashboard_bp.route('/widgets', methods=['GET'])
@jwt_required(optional=True)
def get_widgets():
    """Aggregate dashboard widget definitions from all enabled plugins.

    Consumer for the BasePlugin.get_dashboard_widgets hook. Skips disabled
    plugins, isolates a broken plugin in prod (re-raises in dev/test), and
    returns the merged list sorted by position.
    """
    pm = current_app.extensions.get('plugin_manager')
    if not pm:
        return success_response([])

    widgets = []
    for name, plugin in pm.get_all_plugins().items():
        if not pm.registry.is_enabled(name):
            continue
        try:
            for widget in plugin.get_dashboard_widgets() or []:
                widget['plugin'] = name
                widgets.append(widget)
        except Exception:
            if current_app.config.get('DEBUG') or current_app.config.get('TESTING'):
                raise
            current_app.logger.exception(
                'Plugin %s get_dashboard_widgets failed', name)

    widgets.sort(key=lambda w: w.get('position', 99))
    return success_response(widgets)


@dashboard_bp.route('/health', methods=['GET'])
def health_check():
    """Health check endpoint (no auth required)."""
    from shopdb import __version__

    try:
        db.session.execute(db.text('SELECT 1'))
        db_status = 'healthy'
    except Exception as e:
        db_status = f'unhealthy: {str(e)}'

    return success_response({
        'status': 'ok' if db_status == 'healthy' else 'degraded',
        'database': db_status,
        'version': __version__
    })
