"""Auto-crop postprocessing filter."""

from typing import List, Optional

import numpy as np

from wled_controller.core.filters.base import FilterOptionDef, PostprocessingFilter
from wled_controller.core.filters.image_pool import ImagePool
from wled_controller.core.filters.registry import FilterRegistry
from wled_controller.utils import get_logger

logger = get_logger(__name__)


@FilterRegistry.register
class AutoCropFilter(PostprocessingFilter):
    """Detects and crops black bars (letterboxing/pillarboxing) from the image."""

    filter_id = "auto_crop"
    filter_name = "Auto Crop"
    supports_strip = False

    @classmethod
    def get_options_schema(cls) -> List[FilterOptionDef]:
        return [
            FilterOptionDef(
                key="threshold",
                label="Black Threshold",
                option_type="int",
                default=15,
                min_value=0,
                max_value=50,
                step=1,
            ),
            FilterOptionDef(
                key="min_bar_size",
                label="Min Bar Size (px)",
                option_type="int",
                default=20,
                min_value=0,
                max_value=200,
                step=5,
            ),
            FilterOptionDef(
                key="min_aspect_ratio",
                label="Min Aspect Ratio",
                option_type="float",
                default=0.0,
                min_value=0.0,
                max_value=3.0,
                step=0.01,
            ),
        ]

    def process_image(self, image: np.ndarray, image_pool: ImagePool) -> Optional[np.ndarray]:
        threshold = self.options.get("threshold", 15)
        min_bar_size = self.options.get("min_bar_size", 20)
        min_aspect_ratio = float(self.options.get("min_aspect_ratio", 0.0))

        h, w = image.shape[:2]
        min_h = max(1, h // 10)
        min_w = max(1, w // 10)

        # Compute max channel value per row and per column (vectorized)
        row_max = image.max(axis=(1, 2))  # shape (h,)
        col_max = image.max(axis=(0, 2))  # shape (w,)

        # Scan from top
        top = 0
        while top < h and row_max[top] <= threshold:
            top += 1

        # Scan from bottom
        bottom = h
        while bottom > top and row_max[bottom - 1] <= threshold:
            bottom -= 1

        # Scan from left
        left = 0
        while left < w and col_max[left] <= threshold:
            left += 1

        # Scan from right
        right = w
        while right > left and col_max[right - 1] <= threshold:
            right -= 1

        # Apply min_bar_size: only crop if the detected bar is large enough
        if top < min_bar_size:
            top = 0
        if (h - bottom) < min_bar_size:
            bottom = h
        if left < min_bar_size:
            left = 0
        if (w - right) < min_bar_size:
            right = w

        # Enforce minimum aspect ratio: if the cropped region is too narrow,
        # symmetrically reduce the crop on the tighter axis.
        if min_aspect_ratio > 0:
            cropped_w_cur = right - left
            cropped_h_cur = bottom - top
            if cropped_h_cur > 0:
                ratio = cropped_w_cur / cropped_h_cur
                if ratio < min_aspect_ratio:
                    # Too narrow — widen by reducing left/right crop
                    needed_w = int(cropped_h_cur * min_aspect_ratio)
                    deficit = needed_w - cropped_w_cur
                    expand_left = deficit // 2
                    expand_right = deficit - expand_left
                    left = max(0, left - expand_left)
                    right = min(w, right + expand_right)

        # Safety: don't crop if remaining content is too small
        if (bottom - top) < min_h:
            top, bottom = 0, h
        if (right - left) < min_w:
            left, right = 0, w

        # No crop needed
        if top == 0 and bottom == h and left == 0 and right == w:
            return None

        cropped_h = bottom - top
        cropped_w = right - left
        channels = image.shape[2] if image.ndim == 3 else 3

        result = image_pool.acquire(cropped_h, cropped_w, channels)
        np.copyto(result, image[top:bottom, left:right])
        return result
