"""tag_and_embed / backfill task tests. Models aren't in CI, so we test
the pure helpers (_aggregate_video_predictions, _is_video) as unit tests, and
the DB-touching backfill query as an integration test with monkeypatched
inference.
"""

from pathlib import Path

import pytest

from backend.app.services.ml.tagger import TagPrediction
from backend.app.tasks.ml import _aggregate_video_predictions, _is_video


def test_is_video():
    assert _is_video(Path("a.mp4")) is True
    assert _is_video(Path("a.MKV")) is True
    assert _is_video(Path("a.jpg")) is False


def _pred(name, conf, cat="general"):
    return {name: TagPrediction(name, cat, conf)}


def test_aggregate_video_keeps_corroborated_and_means():
    # #747: 4 frames; "smile" in 3, "sword" in 1 (noise). min_frames=2.
    per_frame = [
        {"smile": TagPrediction("smile", "general", 0.6),
         "sword": TagPrediction("sword", "general", 0.9)},
        _pred("smile", 0.8),
        _pred("smile", 0.7),
        {},
    ]
    out = _aggregate_video_predictions(per_frame, min_frames=2)
    assert "sword" not in out  # one-frame flicker dropped
    assert abs(out["smile"]["confidence"] - (0.6 + 0.8 + 0.7) / 3) < 1e-9  # mean, not max


def test_aggregate_video_clamps_min_frames_to_sample_count():
    # Short video: 1 frame but min_frames=3 — clamp so it still tags.
    out = _aggregate_video_predictions([_pred("solo", 0.8)], min_frames=3)
    assert out["solo"]["confidence"] == 0.8


def test_aggregate_video_empty():
    assert _aggregate_video_predictions([], min_frames=3) == {}


@pytest.mark.integration
@pytest.mark.asyncio
async def test_backfill_enqueues_missing(db, monkeypatch):
    from backend.app.models import ImageRecord
    from backend.app.tasks import ml as ml_tasks

    calls = []
    monkeypatch.setattr(
        ml_tasks.tag_and_embed, "delay", lambda image_id: calls.append(image_id)
    )

    img = ImageRecord(
        path="/images/n.jpg", sha256="n" * 64, size_bytes=1,
        mime="image/jpeg", width=1, height=1,
        origin="imported_filesystem", integrity_status="unknown",
        siglip_embedding=None,
    )
    db.add(img)
    await db.commit()

    count = ml_tasks.backfill()
    assert count >= 1
    assert img.id in calls


@pytest.mark.integration
@pytest.mark.asyncio
async def test_apply_allowlist_applies_above_threshold(db):
    from sqlalchemy import select

    from backend.app.models import ImageRecord, TagAllowlist, TagKind
    from backend.app.models.tag import image_tag
    from backend.app.services.tag_service import TagService
    from backend.app.tasks import ml as ml_tasks
    from tests._prediction_helpers import seed_predictions

    tag = await TagService(db).find_or_create("autohero", TagKind.character)
    db.add(TagAllowlist(tag_id=tag.id, min_confidence=0.95))
    img = ImageRecord(
        path="/images/al.jpg", sha256="al" + "0" * 62, size_bytes=1,
        mime="image/jpeg", width=1, height=1,
        origin="imported_filesystem", integrity_status="unknown",
    )
    db.add(img)
    await db.commit()
    await seed_predictions(
        db, img.id, {"autohero": {"category": "character", "confidence": 0.97}}
    )
    await db.commit()

    n = ml_tasks.apply_allowlist_tags(tag_id=tag.id)
    assert n >= 1
    src = (
        await db.execute(
            select(image_tag.c.source)
            .where(image_tag.c.image_record_id == img.id)
            .where(image_tag.c.tag_id == tag.id)
        )
    ).scalar_one()
    assert src == "ml_auto"


@pytest.mark.integration
@pytest.mark.asyncio
async def test_apply_allowlist_skips_below_threshold(db):
    from sqlalchemy import select

    from backend.app.models import ImageRecord, TagAllowlist, TagKind
    from backend.app.models.tag import image_tag
    from backend.app.services.tag_service import TagService
    from backend.app.tasks import ml as ml_tasks
    from tests._prediction_helpers import seed_predictions

    tag = await TagService(db).find_or_create("lowconf", TagKind.character)
    db.add(TagAllowlist(tag_id=tag.id, min_confidence=0.95))
    img = ImageRecord(
        path="/images/lc.jpg", sha256="lc" + "0" * 62, size_bytes=1,
        mime="image/jpeg", width=1, height=1,
        origin="imported_filesystem", integrity_status="unknown",
    )
    db.add(img)
    await db.commit()
    await seed_predictions(
        db, img.id, {"lowconf": {"category": "character", "confidence": 0.40}}
    )
    await db.commit()
    ml_tasks.apply_allowlist_tags(tag_id=tag.id)
    applied = (
        await db.execute(
            select(image_tag.c.tag_id)
            .where(image_tag.c.image_record_id == img.id)
            .where(image_tag.c.tag_id == tag.id)
        )
    ).scalar_one_or_none()
    assert applied is None
