"""Validity masking and segmentation for frame-level detection."""

import numpy as np


def compute_validity_mask(
    eyelid, scores, pose, *, min_score=0.50, yaw_limit=0.8, require_eyelid=True, verbose=False
):
    """Create per-frame validity mask combining eyelid presence, score, and yaw."""
    n = len(eyelid)
    valid = np.ones(n, dtype=bool)

    if require_eyelid:
        eyelid_present = eyelid > 0
        valid &= eyelid_present
        if verbose:
            print(
                f"  Eyelid present: {eyelid_present.sum()}/{n} frames "
                f"({100 * eyelid_present.mean():.0f}%)"
            )

    if scores is not None:
        score_ok = scores >= min_score
        before = valid.sum()
        valid &= score_ok
        if verbose:
            dropped = before - valid.sum()
            print(f"  Score >= {min_score}: dropped {dropped} frames")

    if pose is not None and yaw_limit > 0:
        yaw = np.abs(pose[:, 1])
        frontal = yaw < yaw_limit
        before = valid.sum()
        valid &= frontal
        if verbose:
            dropped = before - valid.sum()
            print(f"  Yaw < {yaw_limit:.1f} rad: dropped {dropped} frames")

    if verbose:
        print(f"  Valid frames: {valid.sum()}/{n} ({100 * valid.mean():.0f}%)")

    return valid


def filter_by_side(boxes, side, frame_width=640, verbose=False):
    """Return a boolean mask for frames on the chosen side."""
    n = len(boxes)
    mask = np.ones(n, dtype=bool)

    if boxes is not None and side in ("left", "right"):
        cx = (boxes[:, 0] + boxes[:, 2]) / 2
        midpoint = frame_width / 2.0
        if side == "left":
            mask = cx < midpoint
        else:
            mask = cx >= midpoint
        if verbose:
            print(f"  Side filter '{side}': {mask.sum()}/{n} frames " f"({100 * mask.mean():.0f}%)")

    return mask


def segment_valid_regions(valid, min_segment=2, bridge_gap=2):
    """Extract contiguous runs of valid frames, dropping short segments."""
    if bridge_gap > 0:
        bridged = valid.copy()
        i = 0
        n = len(bridged)
        while i < n:
            if not bridged[i]:
                gap_start = i
                while i < n and not bridged[i]:
                    i += 1
                gap_len = i - gap_start
                if gap_len <= bridge_gap and gap_start > 0 and i < n:
                    bridged[gap_start:i] = True
            else:
                i += 1
        working = bridged
    else:
        working = valid

    segments = []
    i = 0
    n = len(working)
    while i < n:
        if working[i]:
            start = i
            while i < n and working[i]:
                i += 1
            length = i - start
            if length >= min_segment:
                segments.append((start, i))
        else:
            i += 1
    return segments
