"""Mouth movement detectors: mouth open and lip activity."""

import numpy as np
from scipy.ndimage import gaussian_filter1d

from ..constants import FPS, LIP_EXPR_INDICES
from ..models import NMSEvent
from ..processing.utils import build_idx_to_frame, mask_to_regions, merge_regions


def detect_mouth_open(
    signal,
    successful_frames,
    valid,
    segments,
    *,
    sigma=1.5,
    threshold=0.06,
    merge_gap=4,
    min_duration_frames=2,
    idx_to_frame=None,
):
    """Detect mouth opening events from jaw_pose pitch."""
    if idx_to_frame is None:
        idx_to_frame = build_idx_to_frame(successful_frames)
    events = []

    for seg_start, seg_end in segments:
        seg = signal[seg_start:seg_end].astype(np.float64)
        if len(seg) < min_duration_frames + 1:
            continue

        smoothed = gaussian_filter1d(seg, sigma=sigma)
        open_mask = smoothed > threshold

        regions = mask_to_regions(open_mask)
        regions = merge_regions(regions, merge_gap)

        for start, end in regions:
            duration = end - start
            if duration < min_duration_frames:
                continue

            seg_slice = smoothed[start:end]
            peak_idx = start + int(np.argmax(seg_slice))
            peak_val = float(smoothed[peak_idx])

            global_start = seg_start + start
            global_end = seg_start + end - 1
            global_peak = seg_start + peak_idx

            amp_score = float(np.clip((peak_val - threshold) / (2 * threshold), 0, 1))
            dur_score = float(np.clip(duration / (FPS * 0.3), 0, 1))
            confidence = 0.6 * amp_score + 0.4 * dur_score

            events.append(
                NMSEvent(
                    tier="MOUTH-OPEN",
                    start_frame=idx_to_frame.get(global_start, global_start),
                    end_frame=idx_to_frame.get(global_end, global_end),
                    peak_frame=idx_to_frame.get(global_peak, global_peak),
                    peak_value=peak_val,
                    duration_ms=duration * (1000.0 / FPS),
                    confidence=float(np.clip(confidence, 0, 1)),
                    label="mouth open",
                )
            )

    return events


def detect_lip_activity(
    expression,
    successful_frames,
    valid,
    segments,
    *,
    sigma=2.0,
    threshold=0.15,
    min_duration_frames=2,
    merge_gap=4,
    idx_to_frame=None,
):
    """Detect lip activity from FLAME expression coefficients."""
    if idx_to_frame is None:
        idx_to_frame = build_idx_to_frame(successful_frames)
    events = []

    if expression is None or expression.shape[1] < max(LIP_EXPR_INDICES) + 1:
        return events

    lip_coeffs = expression[:, LIP_EXPR_INDICES]

    for seg_start, seg_end in segments:
        seg = lip_coeffs[seg_start:seg_end].astype(np.float64)
        if len(seg) < min_duration_frames + 2:
            continue

        velocity = np.diff(seg, axis=0)
        vel_magnitude = np.sqrt(np.sum(velocity**2, axis=1))

        vel_smooth = gaussian_filter1d(vel_magnitude, sigma=sigma)

        vel_padded = np.zeros(len(seg))
        vel_padded[1:] = vel_smooth

        active_mask = vel_padded > threshold

        regions = mask_to_regions(active_mask)
        regions = merge_regions(regions, merge_gap)

        for start, end in regions:
            duration = end - start
            if duration < min_duration_frames:
                continue

            seg_vel = vel_padded[start:end]
            peak_idx = start + int(np.argmax(seg_vel))
            peak_val = float(vel_padded[peak_idx])

            global_start = seg_start + start
            global_end = seg_start + end - 1
            global_peak = seg_start + peak_idx

            mean_vel = float(np.mean(seg_vel))
            amp_score = float(np.clip((mean_vel - threshold) / (2 * threshold), 0, 1))
            dur_score = float(np.clip(duration / (FPS * 0.3), 0, 1))
            confidence = 0.6 * amp_score + 0.4 * dur_score

            events.append(
                NMSEvent(
                    tier="LIP-ACTIVITY",
                    start_frame=idx_to_frame.get(global_start, global_start),
                    end_frame=idx_to_frame.get(global_end, global_end),
                    peak_frame=idx_to_frame.get(global_peak, global_peak),
                    peak_value=peak_val,
                    duration_ms=duration * (1000.0 / FPS),
                    confidence=float(np.clip(confidence, 0, 1)),
                    label="lip movement",
                )
            )

    return events
