# -*- coding: utf-8 -*-
"""
/redo 命令测试：恢复被 /undo 撤销的修改。

覆盖：
1. handle_redo 基本流程：undo 后 redo 恢复文件内容
2. redo 找不到 rewound 事务时返回提示
3. redo 未 undo 的事务应失败
4. redo 后再次 redo 同一事务应失败（rewound=False）
5. redo 新建文件（old_content is None）：从 .bak 恢复文件
6. redo 多文件事务
7. redo 后文件内容等于 AI 编辑后的版本
8. undo→redo→undo 链式操作
9. handle_redo 无 history service 时返回错误
10. handle_redo 无 session_id 时 fallback 到 "default"
"""
import pytest

from abyss.commands.undo import handle_undo, handle_redo, init_history_service
from abyss.history import FileHistoryService


class _FakeSession:
    """测试用的最小 Session。"""

    def __init__(self, session_id: str = "test-session"):
        self.session_id = session_id
        self.messages = []


@pytest.fixture(autouse=True)
def reset_history_service():
    """每个测试前后重置全局单例。"""
    init_history_service(FileHistoryService(storage_dir="__test_redo_reset__"))
    yield
    init_history_service(FileHistoryService(storage_dir="__test_redo_reset__"))


# ---------- handle_redo 基本流程 ----------

def test_redo_restores_file_content(tmp_path):
    """undo 后 redo 应恢复文件到 AI 编辑后的版本。"""
    svc = FileHistoryService(storage_dir=str(tmp_path / "hist"))
    init_history_service(svc)

    target = tmp_path / "file.py"
    target.write_text("original", encoding="utf-8")
    # 模拟 AI 编辑：原内容 "original" → 新内容 "edited"
    tid = svc.begin_transaction("s1", "m1")
    svc.record_file_change(tid, str(target), "original")
    svc.commit_transaction(tid)
    target.write_text("edited", encoding="utf-8")

    session = _FakeSession(session_id="s1")
    # undo：文件回退到 "original"
    r_undo = handle_undo(session)
    assert "[OK]" in r_undo
    assert target.read_text(encoding="utf-8") == "original"

    # redo：文件恢复到 "edited"
    r_redo = handle_redo(session)
    assert "[OK]" in r_redo
    assert "影响文件 1 个" in r_redo
    assert target.read_text(encoding="utf-8") == "edited"


def test_redo_no_rewound_transaction_returns_info(tmp_path):
    """无被撤销的事务时返回 [INFO] 提示。"""
    svc = FileHistoryService(storage_dir=str(tmp_path))
    init_history_service(svc)

    session = _FakeSession(session_id="s1")
    result = handle_redo(session)

    assert "[INFO]" in result
    assert "无可恢复" in result or "/undo" in result


def test_redo_not_rewound_transaction_fails(tmp_path):
    """直接 redo 一个未撤销的事务应失败。"""
    svc = FileHistoryService(storage_dir=str(tmp_path / "hist"))
    init_history_service(svc)

    target = tmp_path / "file.py"
    target.write_text("original", encoding="utf-8")
    tid = svc.begin_transaction("s1", "m1")
    svc.record_file_change(tid, str(target), "original")
    svc.commit_transaction(tid)
    target.write_text("edited", encoding="utf-8")

    # 直接 redo（未先 undo）
    result = svc.redo(tid)

    assert result.success is False
    assert "not rewound" in result.error


def test_redo_same_transaction_twice_fails(tmp_path):
    """redo 后再次 redo 同一事务应失败（rewound 已翻回 False）。"""
    svc = FileHistoryService(storage_dir=str(tmp_path / "hist"))
    init_history_service(svc)

    target = tmp_path / "file.py"
    target.write_text("original", encoding="utf-8")
    tid = svc.begin_transaction("s1", "m1")
    svc.record_file_change(tid, str(target), "original")
    svc.commit_transaction(tid)
    target.write_text("edited", encoding="utf-8")

    # undo → redo → 再 redo（应失败）
    svc.rewind(tid)
    r1 = svc.redo(tid)
    assert r1.success is True

    r2 = svc.redo(tid)
    assert r2.success is False
    assert "not rewound" in r2.error


# ---------- 新建文件 redo ----------

def test_redo_new_file_restores_from_bak(tmp_path):
    """新建文件（old_content is None）：undo 删除后 redo 应从 .bak 恢复文件。"""
    svc = FileHistoryService(storage_dir=str(tmp_path / "hist"))
    init_history_service(svc)

    new_file = tmp_path / "new.py"
    # 模拟 AI 新建文件：old_content=None
    tid = svc.begin_transaction("s1", "m1")
    svc.record_file_change(tid, str(new_file), None)  # None 表示新建
    svc.commit_transaction(tid)
    new_file.write_text("created by AI", encoding="utf-8")

    session = _FakeSession(session_id="s1")
    # undo：文件被删除
    r_undo = handle_undo(session)
    assert "[OK]" in r_undo
    assert not new_file.exists()

    # redo：文件从 .bak 恢复
    r_redo = handle_redo(session)
    assert "[OK]" in r_redo
    assert new_file.exists()
    assert new_file.read_text(encoding="utf-8") == "created by AI"


# ---------- 多文件 redo ----------

def test_redo_multiple_files(tmp_path):
    """redo 多文件事务：所有文件都应恢复。"""
    svc = FileHistoryService(storage_dir=str(tmp_path / "hist"))
    init_history_service(svc)

    a = tmp_path / "a.py"
    b = tmp_path / "b.py"
    a.write_text("a-orig", encoding="utf-8")
    b.write_text("b-orig", encoding="utf-8")

    tid = svc.begin_transaction("s1", "m1")
    svc.record_file_change(tid, str(a), "a-orig")
    svc.record_file_change(tid, str(b), "b-orig")
    svc.commit_transaction(tid)
    a.write_text("a-edited", encoding="utf-8")
    b.write_text("b-edited", encoding="utf-8")

    session = _FakeSession(session_id="s1")
    handle_undo(session)  # 回退到 orig

    r_redo = handle_redo(session)
    assert "[OK]" in r_redo
    assert "影响文件 2 个" in r_redo
    assert a.read_text(encoding="utf-8") == "a-edited"
    assert b.read_text(encoding="utf-8") == "b-edited"


# ---------- 链式操作 ----------

def test_undo_redo_undo_chain(tmp_path):
    """undo → redo → undo 链式操作，每次状态正确。"""
    svc = FileHistoryService(storage_dir=str(tmp_path / "hist"))
    init_history_service(svc)

    target = tmp_path / "file.py"
    target.write_text("original", encoding="utf-8")
    tid = svc.begin_transaction("s1", "m1")
    svc.record_file_change(tid, str(target), "original")
    svc.commit_transaction(tid)
    target.write_text("edited", encoding="utf-8")

    session = _FakeSession(session_id="s1")

    # 第一次 undo：edited → original
    handle_undo(session)
    assert target.read_text(encoding="utf-8") == "original"

    # redo：original → edited
    handle_redo(session)
    assert target.read_text(encoding="utf-8") == "edited"

    # 第二次 undo：edited → original
    handle_undo(session)
    assert target.read_text(encoding="utf-8") == "original"


# ---------- 边界情况 ----------

def test_redo_no_history_service_returns_error():
    """无 history service 时返回错误。"""
    import abyss.commands.undo as undo_mod
    old = undo_mod._HISTORY_SERVICE
    undo_mod._HISTORY_SERVICE = None
    try:
        session = _FakeSession()
        result = handle_redo(session)
        assert "[ERR]" in result
    finally:
        undo_mod._HISTORY_SERVICE = old


def test_redo_no_session_id_falls_back_to_default(tmp_path):
    """session 无 session_id 时回退到 "default"。"""
    svc = FileHistoryService(storage_dir=str(tmp_path / "hist"))
    init_history_service(svc)

    target = tmp_path / "file.py"
    target.write_text("orig", encoding="utf-8")
    # 用 "default" session_id 创建事务（模拟 main.py 行为）
    tid = svc.begin_transaction("default", "m1")
    svc.record_file_change(tid, str(target), "orig")
    svc.commit_transaction(tid)
    target.write_text("edited", encoding="utf-8")

    # 先 undo（用无 session_id 的 session）
    class _SessionNoId:
        messages = []
    r_undo = handle_undo(_SessionNoId())
    assert "[OK]" in r_undo

    # 再 redo
    r_redo = handle_redo(_SessionNoId())
    assert "[OK]" in r_redo
    assert target.read_text(encoding="utf-8") == "edited"


def test_redo_scoped_to_session(tmp_path):
    """redo 按 session 隔离：session A 的 undo 不影响 session B 的 redo。"""
    svc = FileHistoryService(storage_dir=str(tmp_path / "hist"))
    init_history_service(svc)

    # session A 的事务
    a_target = tmp_path / "a.py"
    a_target.write_text("a-orig", encoding="utf-8")
    tid_a = svc.begin_transaction("sess-a", "m1")
    svc.record_file_change(tid_a, str(a_target), "a-orig")
    svc.commit_transaction(tid_a)
    a_target.write_text("a-edited", encoding="utf-8")

    # session B 的事务
    b_target = tmp_path / "b.py"
    b_target.write_text("b-orig", encoding="utf-8")
    tid_b = svc.begin_transaction("sess-b", "m1")
    svc.record_file_change(tid_b, str(b_target), "b-orig")
    svc.commit_transaction(tid_b)
    b_target.write_text("b-edited", encoding="utf-8")

    # session A undo
    session_a = _FakeSession(session_id="sess-a")
    handle_undo(session_a)
    assert a_target.read_text(encoding="utf-8") == "a-orig"

    # session B redo（应失败：session B 没有 rewound 事务）
    session_b = _FakeSession(session_id="sess-b")
    r_b = handle_redo(session_b)
    assert "[INFO]" in r_b  # 无可恢复的撤销

    # b.py 不应被改动
    assert b_target.read_text(encoding="utf-8") == "b-edited"
