"""Steps for three_way_merge_conflict.feature."""

from __future__ import annotations

from behave import given, then, when

from cleveragents.domain.models.planconfig import (
    ConflictContext,
    ConflictReport,
    ConflictType,
    MergeResult,
    ThreeWayMergeConflictDetector,
)

_CT = {
    "MODIFY_MODIFY": ConflictType.MODIFY_MODIFY,
    "MODIFY_DELETE": ConflictType.MODIFY_DELETE,
    "DELETE_MODIFY": ConflictType.DELETE_MODIFY,
    "DELETE_DELETE": ConflictType.DELETE_DELETE,
    "ADD_ADD": ConflictType.ADD_ADD,
}


# ── detector setup ─────────────────────────────────────────────────────────────


@given('a three-way merge where all versions agree on key "{key}" value "{value}"')
def step_all_agree(context, key, value):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={key: value},
        parent={key: value},
        subplan={key: value},
    )


@given(
    'a three-way merge with ancestor "{k1}"="{v1}", parent "{k2}"="{v2}", subplan "{k3}"="{v3}"'
)
def step_three_modify(context, k1, v1, k2, v2, k3, v3):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={k1: v1},
        parent={k2: v2},
        subplan={k3: v3},
    )


@given(
    'a three-way merge with key "{key}", ancestor "{av}", parent changed to "{pv}", subplan deleted it'
)
def step_modify_delete(context, key, av, pv):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={key: av},
        parent={key: pv},
        subplan={},
    )


@given(
    'a three-way merge with key "{key}", ancestor "{av}", parent deleted it, subplan changed to "{sv}"'
)
def step_delete_modify(context, key, av, sv):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={key: av},
        parent={},
        subplan={key: sv},
    )


@given(
    'a three-way merge where ancestor lacks "{key}", parent and subplan both add it as "{value}"'
)
def step_add_same(context, key, value):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={},
        parent={key: value},
        subplan={key: value},
    )


@given(
    'a three-way merge where ancestor lacks "{key}", parent adds "{pv}" and subplan adds "{sv}"'
)
def step_add_different(context, key, pv, sv):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={},
        parent={key: pv},
        subplan={key: sv},
    )


@given(
    'a three-way merge where parent deletes "{key}" and subplan keeps it at "{value}"'
)
def step_parent_deletes_subplan_keeps(context, key, value):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={key: value},
        parent={},
        subplan={key: value},
    )


@given(
    'a three-way merge where subplan deletes "{key}" and parent keeps it at "{value}"'
)
def step_subplan_deletes_parent_keeps(context, key, value):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={key: value},
        parent={key: value},
        subplan={},
    )


@given(
    'a three-way merge where only parent changed key "{key}" from "{av}" to "{pv}", subplan kept "{sv}"'
)
def step_only_parent_changed(context, key, av, pv, sv):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={key: av},
        parent={key: pv},
        subplan={key: sv},
    )


@given(
    'a three-way merge where only subplan changed key "{key}" from "{av}" to "{sv}", parent kept "{pv}"'
)
def step_only_subplan_changed(context, key, av, sv, pv):
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={key: av},
        parent={key: pv},
        subplan={key: sv},
    )


@given("a mixed three-way merge scenario")
def step_mixed_scenario(context):
    # conflict_key: MODIFY_MODIFY (both changed, differently)
    # auto_key:     MODIFY_DELETE auto-resolved to "new" (parent changed, subplan deleted)
    # ok_key:       unchanged in all three → MERGED_OK
    context.detector = ThreeWayMergeConflictDetector(
        ancestor={"conflict_key": "a", "auto_key": "old", "ok_key": "same"},
        parent={"conflict_key": "b", "auto_key": "new", "ok_key": "same"},
        subplan={"conflict_key": "c", "ok_key": "same"},
    )


@given("a three-way merge detector initialized with all None inputs")
def step_none_inputs(context):
    context.detector = ThreeWayMergeConflictDetector(None, None, None)


@given("I directly call analyse_field with all-False has_key flags and None values")
def step_analyse_field_direct(context):
    detector = ThreeWayMergeConflictDetector({}, {}, {})
    context.analyse_result = detector._analyse_field(
        "x",
        None,
        None,
        None,
        ancestor_has_key=False,
        parent_has_key=False,
        subplan_has_key=False,
    )


@given("I import the planconfig package and call its internal helper")
def step_import_planconfig(context):
    from cleveragents.domain.models.planconfig import _get_all_from_plan_config

    context.helper_result = _get_all_from_plan_config()
    # Verify MergeResult is importable from the package (exercises __init__ exports)
    assert MergeResult is not None


# ── ConflictReport setup ───────────────────────────────────────────────────────


@given(
    'a ConflictReport of type "{ctype}" with ancestor "{av}" parent "{pv}" subplan "{sv}"'
)
def step_cr_all_values(context, ctype, av, pv, sv):
    context.report = ConflictReport(
        key="k",
        conflict_type=_CT[ctype],
        context=ConflictContext(ancestor_value=av, parent_value=pv, subplan_value=sv),
    )


@given(
    'a ConflictReport of type "{ctype}" with ancestor "{av}" parent "{pv}" and no subplan'
)
def step_cr_no_subplan(context, ctype, av, pv):
    context.report = ConflictReport(
        key="k",
        conflict_type=_CT[ctype],
        context=ConflictContext(ancestor_value=av, parent_value=pv, subplan_value=None),
    )


@given(
    'a ConflictReport of type "{ctype}" with ancestor "{av}" no parent and subplan "{sv}"'
)
def step_cr_no_parent(context, ctype, av, sv):
    context.report = ConflictReport(
        key="k",
        conflict_type=_CT[ctype],
        context=ConflictContext(ancestor_value=av, parent_value=None, subplan_value=sv),
    )


@given(
    'a ConflictReport of type "{ctype}" with no ancestor parent "{pv}" subplan "{sv}"'
)
def step_cr_no_ancestor(context, ctype, pv, sv):
    context.report = ConflictReport(
        key="k",
        conflict_type=_CT[ctype],
        context=ConflictContext(ancestor_value=None, parent_value=pv, subplan_value=sv),
    )


# ── action steps ───────────────────────────────────────────────────────────────


@when("I run the three-way merge detector")
def step_run_detector(context):
    context.result = context.detector.detect()


@when("I call detect_diff_text")
def step_call_diff_text(context):
    context.diff_text = context.detector.detect_diff_text()


@when("I call describe_all")
def step_call_describe_all(context):
    context.summary = context.detector.describe_all()


@when("I check the resolved_value")
def step_check_resolved(context):
    context.resolved = context.report.resolved_value


# ── assertion steps ────────────────────────────────────────────────────────────


@then("the merge should have no conflicts")
def step_no_conflicts(context):
    assert len(context.result.conflicts) == 0, (
        f"Expected no conflicts but got: {context.result.conflicts}"
    )


@then('the merged config should contain key "{key}" with value "{value}"')
def step_merged_contains(context, key, value):
    assert key in context.result.merged_config, (
        f"Key '{key}' not in merged_config: {context.result.merged_config}"
    )
    assert context.result.merged_config[key] == value, (
        f"Expected '{value}' but got '{context.result.merged_config[key]}'"
    )


@then("the merge should have {n:d} conflict")
def step_n_conflicts(context, n):
    actual = len(context.result.conflicts)
    assert actual == n, f"Expected {n} conflict(s) but got {actual}"


@then('the conflict key is "{key}" with type "{ctype}"')
def step_conflict_key_type(context, key, ctype):
    assert any(
        c.key == key and str(c.conflict_type) == ctype for c in context.result.conflicts
    ), f"No conflict key='{key}' type='{ctype}' in {context.result.conflicts}"


@then("the conflict has no auto-resolved entries")
def step_no_auto_resolved(context):
    assert len(context.result.auto_resolved) == 0, (
        f"Expected empty auto_resolved but got: {context.result.auto_resolved}"
    )


@then('key "{key}" is auto-resolved to "{value}"')
def step_auto_resolved_to(context, key, value):
    assert key in context.result.auto_resolved, (
        f"Key '{key}' not in auto_resolved: {context.result.auto_resolved}"
    )
    assert context.result.auto_resolved[key] == value, (
        f"Expected '{value}' but got '{context.result.auto_resolved[key]}'"
    )


@then('the merged config should not contain key "{key}"')
def step_not_in_merged(context, key):
    assert key not in context.result.merged_config, (
        f"Key '{key}' unexpectedly present in merged_config: {context.result.merged_config}"
    )


@then('the diff text should say "No conflicts detected"')
def step_diff_no_conflicts(context):
    assert "No conflicts detected" in context.diff_text, (
        f"Expected 'No conflicts detected' in:\n{context.diff_text!r}"
    )


@then('the diff text should contain "{text}"')
def step_diff_contains(context, text):
    assert text in context.diff_text, (
        f"Expected '{text}' in diff text:\n{context.diff_text}"
    )


@then("the summary should contain a CONFLICT entry")
def step_summary_conflict(context):
    assert any(e["status"] == "CONFLICT" for e in context.summary), (
        f"No CONFLICT entry in: {context.summary}"
    )


@then("the summary should contain an AUTO_RESOLVED entry")
def step_summary_auto_resolved(context):
    assert any(e["status"] == "AUTO_RESOLVED" for e in context.summary), (
        f"No AUTO_RESOLVED entry in: {context.summary}"
    )


@then("the summary should contain a MERGED_OK entry")
def step_summary_merged_ok(context):
    assert any(e["status"] == "MERGED_OK" for e in context.summary), (
        f"No MERGED_OK entry in: {context.summary}"
    )


@then("the resolved_value should be None")
def step_resolved_none(context):
    assert context.resolved is None, f"Expected None but got: {context.resolved!r}"


@then('the resolved_value should equal "{value}"')
def step_resolved_equals(context, value):
    assert context.resolved == value, (
        f"Expected '{value}' but got: {context.resolved!r}"
    )


@then('the analyse_field result type should be "{ctype}"')
def step_analyse_result_type(context, ctype):
    assert str(context.analyse_result.conflict_type) == ctype, (
        f"Expected '{ctype}' but got: {context.analyse_result.conflict_type!r}"
    )


@then("the helper returns a non-empty list")
def step_helper_non_empty(context):
    assert isinstance(context.helper_result, list) and len(context.helper_result) > 0, (
        f"Expected non-empty list but got: {context.helper_result!r}"
    )
