"""M4 E2E merge verification tests.

Tests three-way merge for non-conflicting changes and conflict surfacing.
Extracted from ``helper_m4_e2e_domain`` to keep that module well under
the 500-line limit (CONTRIBUTING.md compliance).

Usage (via dispatcher):
    python robot/helper_m4_e2e_verification.py merge-clean
    python robot/helper_m4_e2e_verification.py merge-conflict
"""

from __future__ import annotations

import shutil
import sys
from pathlib import Path

# Ensure the src directory is on the import path.
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.append(_SRC)

from helper_m4_e2e_common import _fail  # noqa: E402

from cleveragents.domain.models.core.plan import SubplanMergeStrategy  # noqa: E402
from cleveragents.infrastructure.sandbox.merge import (  # noqa: E402
    GitMergeStrategy,
    MergeResult,
    SequentialMergeStrategy,
)


def _assert_git_available() -> None:
    """Pre-check that ``git`` is available on ``$PATH``.

    ``GitMergeStrategy.merge()`` shells out to ``git merge-file``.  If
    ``git`` is absent, the fallback to ``SequentialMergeStrategy``
    produces results that fail the merge assertions with misleading
    diagnostics (e.g. "missing ours change" instead of "git not found").
    """
    if shutil.which("git") is None:
        _fail("git is required on PATH for merge tests")


# ---------------------------------------------------------------------------
# merge-clean
# ---------------------------------------------------------------------------


def merge_clean() -> None:
    """Verify three-way merge combines non-conflicting changes."""
    _assert_git_available()

    strategy = GitMergeStrategy()

    base = "line1\nline2\nline3\nline4\nline5\n"
    ours = "line1-modified-by-subplan-A\nline2\nline3\nline4\nline5\n"
    theirs = "line1\nline2\nline3\nline4\nline5-modified-by-subplan-B\n"

    result = strategy.merge(base, ours, theirs)

    if not result.success:
        _fail(f"clean merge should succeed, got conflicts: {result.has_conflicts}")
    if result.has_conflicts:
        _fail("clean merge should have no conflicts")
    if "line1-modified-by-subplan-A" not in result.content:
        _fail("merged content missing ours change")
    if "line5-modified-by-subplan-B" not in result.content:
        _fail("merged content missing theirs change")
    if result.conflict_markers:
        _fail(f"should have no conflict markers, got {result.conflict_markers}")

    # Also verify SequentialMergeStrategy (last-wins)
    seq_strategy = SequentialMergeStrategy()
    seq_result = seq_strategy.merge(base, ours, theirs)
    if not seq_result.success:
        _fail("sequential merge should always succeed")
    if seq_result.content != theirs:
        _fail("sequential merge should return theirs")

    # Verify merge result dataclass fields
    clean_result = MergeResult(success=True, content="merged", has_conflicts=False)
    if clean_result.success is not True:
        _fail("MergeResult success field incorrect")
    if clean_result.has_conflicts is not False:
        _fail("MergeResult has_conflicts field incorrect")

    print("m4-merge-clean-ok")


# ---------------------------------------------------------------------------
# merge-conflict
# ---------------------------------------------------------------------------


def merge_conflict() -> None:
    """Verify merge conflicts are surfaced correctly."""
    _assert_git_available()

    strategy = GitMergeStrategy()

    base = "line1\nline2\nline3\n"
    ours = "line1\nline2-ours\nline3\n"
    theirs = "line1\nline2-theirs\nline3\n"

    result = strategy.merge(base, ours, theirs)

    if result.success:
        _fail("conflicting merge should not succeed")
    if not result.has_conflicts:
        _fail("conflicting merge should have conflicts")
    if not result.conflict_markers:
        _fail("conflict markers should be present")

    # Verify conflict markers contain standard git markers
    if "<<<<<<<" not in result.content:
        _fail("merged content should contain <<<<<<< marker")
    if ">>>>>>>" not in result.content:
        _fail("merged content should contain >>>>>>> marker")
    if "=======" not in result.content:
        _fail("merged content should contain ======= marker")

    # Verify conflict_markers list has at least one region
    for start, end in result.conflict_markers:
        if start < 1:
            _fail(f"conflict marker start should be >= 1, got {start}")
        if end < start:
            _fail(f"conflict marker end ({end}) should be >= start ({start})")

    # Verify SubplanMergeStrategy enum values
    if SubplanMergeStrategy.FAIL_ON_CONFLICT != "fail_on_conflict":
        _fail("FAIL_ON_CONFLICT value mismatch")

    expected_strategies = {
        "git_three_way",
        "sequential_apply",
        "fail_on_conflict",
        "last_wins",
    }
    actual_strategies = {s.value for s in SubplanMergeStrategy}
    if actual_strategies != expected_strategies:
        _fail(
            f"merge strategies mismatch: {actual_strategies} != {expected_strategies}"
        )

    print("m4-merge-conflict-ok")
