"""
Coverage tests for role_classifier.py.
Targets: 30, 37, 71, 80, 89, 91, 93, 95, 101-106, 119-123, 129, 131, 133,
         139, 141, 156, 159, 162, 174, 208, 227-303.
"""
from __future__ import annotations

from mjr_am_backend.features.geninfo import role_classifier as rc

# ─── _extract_workflow_metadata ─────────────────────────────────────────────

def test_extract_workflow_metadata_no_extra():
    assert rc._extract_workflow_metadata({}) == {}


def test_extract_workflow_metadata_with_extra():
    wf = {"extra": {"title": "My Workflow", "author": "Test"}}
    meta = rc._extract_workflow_metadata(wf)
    assert meta["title"] == "My Workflow" and meta["author"] == "Test"


def test_extract_workflow_metadata_non_dict():
    assert rc._extract_workflow_metadata(None) == {}
    assert rc._extract_workflow_metadata("string") == {}


# ─── _subject_role_hints ────────────────────────────────────────────────────

def test_subject_role_hints_not_dict():
    assert rc._subject_role_hints(None) == set()


def test_subject_role_hints_first_frame():
    subject = {"_meta": {"title": "First Frame"}, "inputs": {"image": "frame_first.png"}}
    roles = rc._subject_role_hints(subject)
    assert "first_frame" in roles


def test_subject_role_hints_control():
    subject = {"_meta": {"title": "ControlNet Input"}, "inputs": {"image": ""}}
    roles = rc._subject_role_hints(subject)
    assert "control" in roles


def test_subject_role_hints_mask():
    subject = {"_meta": {"title": "Inpaint Mask"}, "inputs": {"image": ""}}
    roles = rc._subject_role_hints(subject)
    assert "mask/inpaint" in roles


def test_subject_role_hints_style_reference():
    subject = {"title": "Reference Style", "inputs": {"image": ""}}
    roles = rc._subject_role_hints(subject)
    assert "style/reference" in roles


# ─── _classify_control_or_mask_role ─────────────────────────────────────────

def test_classify_control_ipadapter():
    assert rc._classify_control_or_mask_role("ipadapterapply", "image", False) == "style/reference"


def test_classify_control_controlnet_image():
    assert rc._classify_control_or_mask_role("controlnetapply", "image", False) == "control_image"


def test_classify_control_controlnet_video():
    assert rc._classify_control_or_mask_role("controlnetapply", "image", True) == "control_video"


def test_classify_control_mask():
    assert rc._classify_control_or_mask_role("inpaintnode", "mask_input", False) == "mask/inpaint"


def test_classify_control_depth():
    assert rc._classify_control_or_mask_role("depthestimator", "depth_image", False) == "depth"


def test_classify_control_none():
    assert rc._classify_control_or_mask_role("somenode", "image", False) is None


# ─── _classify_vace_or_range_role ───────────────────────────────────────────

def test_classify_vace_control():
    assert rc._classify_vace_or_range_role("vacenode", "control_input") == "control_video"


def test_classify_vace_source():
    assert rc._classify_vace_or_range_role("wanvaceencoder", "image") == "source"


def test_classify_frame_range():
    assert rc._classify_vace_or_range_role("starttoendnode", "start_frame") == "first_frame"


def test_classify_none():
    assert rc._classify_vace_or_range_role("saveimage", "image") is None


# ─── _frame_edge_role ───────────────────────────────────────────────────────

def test_frame_edge_role_first():
    assert rc._frame_edge_role("start_frame") == "first_frame"
    assert rc._frame_edge_role("first_image") == "first_frame"


def test_frame_edge_role_last():
    assert rc._frame_edge_role("end_frame") == "last_frame"
    assert rc._frame_edge_role("last_image") == "last_frame"


def test_frame_edge_role_none():
    assert rc._frame_edge_role("image") is None


# ─── _classify_generic_source_role ──────────────────────────────────────────

def test_classify_generic_source_first_frame():
    assert rc._classify_generic_source_role("somenode", "first_image") == "first_frame"


def test_classify_generic_source_last_frame():
    assert rc._classify_generic_source_role("somenode", "last_image") == "last_frame"


def test_classify_generic_source_img2vid():
    assert rc._classify_generic_source_role("img2videncoder", "image") == "source"


def test_classify_generic_source_sampler_image():
    assert rc._classify_generic_source_role("samplernode", "image_input") == "source"


def test_classify_generic_source_none():
    assert rc._classify_generic_source_role("unknownnode", "x") is None


# ─── _classify_downstream_input_role ────────────────────────────────────────

def test_classify_downstream_controlnet():
    role, continue_chain = rc._classify_downstream_input_role("controlnetapply", "image", False)
    assert role == "control_image" and continue_chain is False


def test_classify_downstream_none():
    role, continue_chain = rc._classify_downstream_input_role("unknownnode", "x", False)
    assert role is None and continue_chain is True


# ─── _contains_any_token ────────────────────────────────────────────────────

def test_contains_any_token():
    assert rc._contains_any_token("first frame ref", ("first",)) is True
    assert rc._contains_any_token("normal image", ("first",)) is False


# ─── _detect_input_role ─────────────────────────────────────────────────────

def test_detect_input_role_no_downstream():
    nodes = {"1": {"class_type": "LoadImage", "inputs": {"image": "x.png"}}}
    role = rc._detect_input_role(nodes, "1")
    assert role in {"input", "first_frame", "last_frame", "source"}
