"""Tests de crop_to_content (offline, sin red ni GPU; PIL/numpy)."""

import os
import sys

import numpy as np
from PIL import Image

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from ml.crop_to_content import crop_to_content  # noqa: E402


def _rgba_subject_in_corner(canvas=256, box=40, ox=8, oy=8):
    """RGBA con un rectangulo opaco rojo en una esquina, resto transparente."""
    arr = np.zeros((canvas, canvas, 4), dtype=np.uint8)
    arr[oy:oy + box, ox:ox + box, 0] = 220   # R
    arr[oy:oy + box, ox:ox + box, 3] = 255   # alpha opaco
    return Image.fromarray(arr, "RGBA")


def _rgba_subject_centered(canvas=256, fill_ratio=0.9):
    """RGBA con un rectangulo opaco que llena ~fill_ratio del lienzo, centrado."""
    arr = np.zeros((canvas, canvas, 4), dtype=np.uint8)
    side = int(canvas * fill_ratio)
    o = (canvas - side) // 2
    arr[o:o + side, o:o + side, 1] = 200     # G
    arr[o:o + side, o:o + side, 3] = 255
    return Image.fromarray(arr, "RGBA")


def _rgb_subject_on_bg(canvas=200, box=50, ox=10, oy=10, bg=(255, 255, 255)):
    """RGB con un cuadrado de color sobre fondo plano (sin alpha)."""
    arr = np.zeros((canvas, canvas, 3), dtype=np.uint8)
    arr[:, :] = bg
    arr[oy:oy + box, ox:ox + box] = (0, 0, 200)  # sujeto azul
    return Image.fromarray(arr, "RGB")


def _alpha_bbox_coverage(img, threshold=10):
    """Fraccion del lado que ocupa el bbox del contenido (alpha>threshold)."""
    a = np.asarray(img.convert("RGBA"))[..., 3]
    ys, xs = np.where(a > threshold)
    if xs.size == 0:
        return 0.0
    bw = xs.max() - xs.min() + 1
    bh = ys.max() - ys.min() + 1
    return max(bw, bh) / max(img.size)


def test_golden_corner_subject_fills_frame():
    """Sujeto en la esquina -> tras crop ocupa casi todo el frame (square)."""
    img = _rgba_subject_in_corner()
    before = _alpha_bbox_coverage(img)
    out = crop_to_content(img, pad_ratio=0.06, square=True)
    after = _alpha_bbox_coverage(out)
    assert out.mode == "RGBA"
    assert out.size[0] == out.size[1]          # cuadrado
    assert before < 0.25                       # antes diminuto
    assert after >= 0.80                       # despues llena el frame


def test_edge_centered_subject_not_overcropped():
    """Sujeto ya centrado que llena ~90%: la cobertura se mantiene alta, no se rompe."""
    img = _rgba_subject_centered(fill_ratio=0.9)
    out = crop_to_content(img, pad_ratio=0.06, square=True)
    assert out.size[0] == out.size[1]
    assert _alpha_bbox_coverage(out) >= 0.80


def test_edge_rgb_background_bbox():
    """RGB con fondo plano: detecta el sujeto por diff-fondo y lo cuadra."""
    img = _rgb_subject_on_bg()
    out = crop_to_content(img, pad_ratio=0.05, square=True)
    assert out.mode == "RGB"
    assert out.size[0] == out.size[1]
    # El sujeto azul debe ocupar buena parte del lienzo recortado.
    arr = np.asarray(out)
    is_subject = (arr[..., 2] > 120) & (arr[..., 0] < 80)
    cov = is_subject.sum() / (out.size[0] * out.size[1])
    assert cov >= 0.4


def test_edge_no_square_only_crops():
    """square=False: recorta al bbox + margen, sin forzar cuadrado."""
    img = _rgba_subject_in_corner(box=40)
    out = crop_to_content(img, pad_ratio=0.0, square=False)
    # bbox del sujeto es 40x40 -> sin pad ni cuadrar, sale 40x40.
    assert out.size == (40, 40)


def test_error_all_transparent_returns_copy():
    """Imagen toda transparente: no crashea, devuelve copia intacta (mismo tamano)."""
    arr = np.zeros((128, 128, 4), dtype=np.uint8)  # alpha 0 en todo
    img = Image.fromarray(arr, "RGBA")
    out = crop_to_content(img)
    assert out.size == (128, 128)
    assert np.asarray(out)[..., 3].max() == 0


def test_error_none_raises():
    try:
        crop_to_content(None)
        assert False, "deberia lanzar ValueError"
    except ValueError as e:
        assert "None" in str(e)


def test_does_not_mutate_input():
    img = _rgba_subject_in_corner()
    snapshot = np.asarray(img).copy()
    crop_to_content(img)
    assert np.array_equal(np.asarray(img), snapshot)
