"""CROWD admission guards and side-effect-free future Runtime projection seams.

No boolean flag can bypass the predevelopment gate. Removing the gate requires
an explicitly reviewed Runtime change after actual model/export verification.
"""
from copy import deepcopy

from app.crowd_config import CROWD_MODEL, CrowdConfig, FULL_FRAME

MODEL_PENDING = 'CROWD_NEEDS_MODEL_CONTRACT_CONFIRMATION'
STANDALONE_REQUIRED = 'CROWD_STANDALONE_REQUIRED'
TRACKING_UNSUPPORTED = 'CROWD_TRACKING_UNSUPPORTED'


def _request(scenarios):
    found = [s for s in scenarios if s.get('scenarioCode') == 'CROWD']
    if len(found) != 1:
        raise ValueError('CROWD_REQUEST_REQUIRED')
    request = found[0]
    if type(request.get('enabled')) is not bool:
        raise ValueError('CROWD_INVALID_ENABLED')
    return request


def validate_web_request(scenarios):
    """After the existing six-scenario Web schema validation, before admission."""
    request = _request(scenarios)
    if request['enabled']:
        if any(s.get('enabled') and s.get('scenarioCode') != 'CROWD' for s in scenarios):
            raise ValueError(STANDALONE_REQUIRED)


def validate_standalone(spec, config):
    if not config.enabled:
        return
    if not spec.get('server_id') or not spec.get('source_id'):
        raise ValueError('CROWD_CANONICAL_IDENTITY_REQUIRED')
    if any((spec.get(k) or {}).get('enabled', False)
           for k in ('intrusion', 'loitering', 'fall', 'fire', 'fight')):
        raise ValueError(STANDALONE_REQUIRED)
    pose = spec.get('pose') or {}
    if any(pose.get(k, False) for k in ('enabled', 'show_joints', 'show_edges')):
        raise ValueError(STANDALONE_REQUIRED)
    profile = spec.get('inference_profile') or {}
    if profile.get('roles', ['det']) != ['det'] or profile.get('det_model', CROWD_MODEL) != CROWD_MODEL:
        raise ValueError(STANDALONE_REQUIRED)
    # Local settings must be explicit. Do not silently ignore Tracking=true.
    if (spec.get('tracking') or {}).get('enabled', True) is not False:
        raise ValueError(TRACKING_UNSUPPORTED)


def crowd_plan(spec):
    """Called before existing person/fire execution planning can silently fallback."""
    raw = spec.get('crowd')
    requested_model = (spec.get('inference_profile') or {}).get('det_model')
    if raw is None and requested_model != CROWD_MODEL:
        return
    config = CrowdConfig.parse({} if raw is None else raw)
    if config.enabled:
        validate_standalone(spec, config)
        if spec.get('store', True) or spec.get('active_learning', True):
            raise ValueError('CROWD_STORAGE_UNSUPPORTED')
        return dict(category='crowd', model=CROWD_MODEL, role='det', outputs=['bbox'])
    if requested_model == CROWD_MODEL:
        raise ValueError(MODEL_PENDING)


def project_crowd_candidate(spec, scenarios):
    """Build a private candidate, not an applied/started Stream spec.

    Web exposes one confidence: an explicit value applies to both classes.
    Null preserves AI-owned class-specific thresholds (default .45/.45).
    Web has no Tracking field: CROWD-only projection deliberately selects OFF.
    Settings/Start persistence and actual Runtime wiring remain gated.
    """
    request = _request(scenarios)
    candidate = deepcopy(spec)
    if not request['enabled'] and 'crowd' not in candidate:
        return candidate
    if request['enabled'] and any(s.get('enabled') and s.get('scenarioCode') != 'CROWD' for s in scenarios):
        raise ValueError(STANDALONE_REQUIRED)
    current = CrowdConfig.parse(candidate.get('crowd') or {})
    changes = dict(enabled=request['enabled'])
    confidence = request.get('confidence')
    if confidence is not None:
        changes.update(person_confidence=confidence, head_confidence=confidence)
    rois = request.get('rois') or []
    first = rois[0] if rois else None
    changes.update(roi=first['points'] if first else FULL_FRAME,
                   roi_name=first['label'] if first else 'Crowd ROI')
    configured = current.patched(changes)
    candidate['crowd'] = configured.settings_payload()
    if configured.enabled:
        # An existing saved ON scenario is not silently disabled by this seam.
        candidate['tracking'] = dict(candidate.get('tracking') or {}, enabled=False)
        profile = dict(candidate.get('inference_profile') or {})
        profile.update(roles=['det'], det_model=CROWD_MODEL)
        candidate['inference_profile'] = profile
        candidate['visualization'] = dict(candidate.get('visualization') or {}, categories=['person','head'])
        validate_standalone(candidate, configured)
    elif (candidate.get('inference_profile') or {}).get('det_model') == CROWD_MODEL:
        candidate['inference_profile'] = dict(candidate['inference_profile'], roles=['det'],det_model='RFDETRMedium')
        candidate['visualization'] = dict(candidate.get('visualization') or {},categories=['person'])
    return candidate


def patch_crowd_candidate(spec, changes):
    """Private configuration helper; no HTTP/CAS/persistence side effects."""
    current = CrowdConfig.parse(spec.get('crowd') or {})
    configured = current.patched(changes)
    candidate = deepcopy(spec)
    if configured != current:
        candidate['crowd'] = configured.settings_payload()
    validate_standalone(candidate, configured)
    return candidate
