#!/usr/bin/env python3
"""done-watcher.py 테스트

테스트 항목:
1. extract_team_from_done_file() - .done 파일에서 팀 추출
2. set_bot_idle() - 봇 idle 전환
3. scan_done_files() - .done 파일 스캔
4. process_done_files() - .done 파일 처리
"""

import importlib
import json
import sys
from pathlib import Path

import pytest

# 테스트 대상 모듈 import
sys.path.insert(0, "/home/jay/workspace/scripts")
done_watcher = importlib.import_module("done-watcher")


class TestExtractTeamFromDoneFile:
    """extract_team_from_done_file() 테스트"""

    def test_extract_from_filename(self, tmp_path, monkeypatch):
        """파일명에서 팀 추출 (task-648.1.dev1.done → dev1)"""
        monkeypatch.setattr(done_watcher, "WORKSPACE_ROOT", str(tmp_path))

        done_file = tmp_path / "task-648.1.dev1.done"
        done_file.touch()

        result = done_watcher.extract_team_from_done_file(done_file)
        assert result == "dev1"

    def test_extract_from_filename_dev2(self, tmp_path, monkeypatch):
        """파일명에서 팀 추출 (task-648.1.dev2.done → dev2)"""
        monkeypatch.setattr(done_watcher, "WORKSPACE_ROOT", str(tmp_path))

        done_file = tmp_path / "task-648.1.dev2.done"
        done_file.touch()

        result = done_watcher.extract_team_from_done_file(done_file)
        assert result == "dev2"

    def test_extract_without_team_in_filename(self, tmp_path, monkeypatch):
        """팀 정보가 없는 파일명 → task-timers.json에서 조회"""
        monkeypatch.setattr(done_watcher, "WORKSPACE_ROOT", str(tmp_path))

        # task-timers.json 생성
        timer_data = {
            "tasks": {"task-648.1": {"team_id": "dev3", "status": "processing", "started_at": "2026-03-17T07:00:00Z"}}
        }
        (tmp_path / "memory").mkdir(parents=True, exist_ok=True)
        (tmp_path / "memory" / "task-timers.json").write_text(json.dumps(timer_data, indent=2), encoding="utf-8")

        # .done 파일 생성
        done_file = tmp_path / "task-648.1.done"
        done_file.touch()

        result = done_watcher.extract_team_from_done_file(done_file)
        assert result == "dev3"


class TestSetBotIdle:
    """set_bot_idle() 테스트"""

    def test_set_idle_success(self, tmp_path, monkeypatch):
        """idle 전환 성공"""
        monkeypatch.setattr(done_watcher, "BOT_ACTIVITY_FILE", tmp_path / "bot-activity.json")
        monkeypatch.setattr(done_watcher, "DONE_PROTOCOL_LOG", tmp_path / "done-protocol.log")

        # 초기 데이터
        data = {"bots": {"dev1": {"status": "processing", "since": "2026-03-17T06:54:35Z"}}}
        (tmp_path / "bot-activity.json").write_text(json.dumps(data, indent=2), encoding="utf-8")

        result = done_watcher.set_bot_idle("dev1")
        assert result is True

        # 상태 확인
        updated = json.loads((tmp_path / "bot-activity.json").read_text(encoding="utf-8"))
        assert updated["bots"]["dev1"]["status"] == "idle"

    def test_set_idle_already_idle(self, tmp_path, monkeypatch):
        """이미 idle인 경우 스킵"""
        monkeypatch.setattr(done_watcher, "BOT_ACTIVITY_FILE", tmp_path / "bot-activity.json")
        monkeypatch.setattr(done_watcher, "DONE_PROTOCOL_LOG", tmp_path / "done-protocol.log")

        # 이미 idle
        data = {"bots": {"dev1": {"status": "idle", "since": "2026-03-17T06:54:35Z"}}}
        (tmp_path / "bot-activity.json").write_text(json.dumps(data, indent=2), encoding="utf-8")

        result = done_watcher.set_bot_idle("dev1")
        assert result is True

    def test_set_idle_unknown_team(self, tmp_path, monkeypatch):
        """알 수 없는 팀"""
        monkeypatch.setattr(done_watcher, "BOT_ACTIVITY_FILE", tmp_path / "bot-activity.json")
        monkeypatch.setattr(done_watcher, "DONE_PROTOCOL_LOG", tmp_path / "done-protocol.log")

        data = {"bots": {"dev1": {"status": "processing", "since": "2026-03-17T06:54:35Z"}}}
        (tmp_path / "bot-activity.json").write_text(json.dumps(data, indent=2), encoding="utf-8")

        result = done_watcher.set_bot_idle("unknown_team")
        assert result is False


class TestScanDoneFiles:
    """scan_done_files() 테스트"""

    def test_scan_done_files(self, tmp_path, monkeypatch):
        """.done 파일 스캔"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)

        # .done 파일 생성
        (tmp_path / "task-648.1.dev1.done").touch()
        (tmp_path / "task-648.2.dev2.done").touch()
        (tmp_path / "task-648.3.done").touch()

        # 처리된 파일 (제외 대상)
        (tmp_path / "task-648.4.dev3.done.acked").touch()

        result = done_watcher.scan_done_files()
        assert len(result) == 3

    def test_scan_no_done_files(self, tmp_path, monkeypatch):
        """.done 파일이 없는 경우"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)

        result = done_watcher.scan_done_files()
        assert len(result) == 0


class TestProcessDoneFiles:
    """process_done_files() 테스트"""

    def test_process_done_files(self, tmp_path, monkeypatch):
        """.done 파일 처리 → bot idle 전환"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)
        monkeypatch.setattr(done_watcher, "BOT_ACTIVITY_FILE", tmp_path / "bot-activity.json")
        monkeypatch.setattr(done_watcher, "DONE_PROTOCOL_LOG", tmp_path / "done-protocol.log")
        monkeypatch.setattr(done_watcher, "WORKSPACE_ROOT", str(tmp_path))

        # bot-activity.json 생성
        data = {
            "bots": {
                "dev1": {"status": "processing", "since": "2026-03-17T06:54:35Z"},
                "dev2": {"status": "processing", "since": "2026-03-17T06:54:36Z"},
            }
        }
        (tmp_path / "bot-activity.json").write_text(json.dumps(data, indent=2), encoding="utf-8")

        # .done 파일 생성
        (tmp_path / "task-648.1.dev1.done").touch()
        (tmp_path / "task-648.2.dev2.done").touch()

        # 처리 실행
        processed = done_watcher.process_done_files()
        assert processed == 2

        # 상태 확인
        updated = json.loads((tmp_path / "bot-activity.json").read_text(encoding="utf-8"))
        assert updated["bots"]["dev1"]["status"] == "idle"
        assert updated["bots"]["dev2"]["status"] == "idle"


class TestScanFailedFiles:
    """scan_failed_files() 테스트"""

    def test_scan_failed_files(self, tmp_path, monkeypatch):
        """.failed 파일 스캔"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)
        # .failed 파일 생성
        (tmp_path / "task-100.failed").write_text('{"task_id":"task-100","fail_reason":"QC FAIL"}')
        (tmp_path / "task-200.failed").write_text('{"task_id":"task-200","fail_reason":"QC FAIL"}')
        result = done_watcher.scan_failed_files()
        assert len(result) == 2

    def test_scan_failed_excludes_acked(self, tmp_path, monkeypatch):
        """.failed.acked 마커가 있으면 제외"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)
        (tmp_path / "task-100.failed").write_text('{"task_id":"task-100","fail_reason":"QC FAIL"}')
        (tmp_path / "task-100.failed.acked").touch()  # acked 마커
        result = done_watcher.scan_failed_files()
        assert len(result) == 0

    def test_scan_no_failed_files(self, tmp_path, monkeypatch):
        """.failed 파일이 없는 경우"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)
        result = done_watcher.scan_failed_files()
        assert len(result) == 0


class TestProcessFailedFiles:
    """process_failed_files() 테스트"""

    def test_process_failed_files_no_bot_token(self, tmp_path, monkeypatch):
        """.failed 파일 처리 - bot token 없으면 알림 실패, rename 안됨"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)
        monkeypatch.setattr(done_watcher, "DONE_PROTOCOL_LOG", tmp_path / "done-protocol.log")
        monkeypatch.delenv("ANU_BOT_TOKEN", raising=False)

        (tmp_path / "task-100.failed").write_text('{"task_id":"task-100","fail_reason":"QC FAIL","timestamp":"2026-04-20T10:00:00"}')

        processed = done_watcher.process_failed_files()
        assert processed == 0
        # .failed 파일이 rename되지 않고 그대로 있어야 함
        assert (tmp_path / "task-100.failed").exists()

    def test_process_failed_files_json_error(self, tmp_path, monkeypatch):
        """.failed 파일 JSON 파싱 실패 시 건너뜀"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)
        monkeypatch.setattr(done_watcher, "DONE_PROTOCOL_LOG", tmp_path / "done-protocol.log")

        (tmp_path / "task-100.failed").write_text("not json content")

        processed = done_watcher.process_failed_files()
        assert processed == 0

    def test_process_failed_files_with_mock_send(self, tmp_path, monkeypatch):
        """.failed 파일 처리 성공 시 .failed.acked로 rename"""
        monkeypatch.setattr(done_watcher, "EVENTS_DIR", tmp_path)
        monkeypatch.setattr(done_watcher, "DONE_PROTOCOL_LOG", tmp_path / "done-protocol.log")
        # send_telegram_notification을 mock하여 성공 반환
        def mock_send(message: str) -> bool:
            assert "QC FAIL" in message
            return True
        monkeypatch.setattr(done_watcher, "send_telegram_notification", mock_send)

        (tmp_path / "task-100.failed").write_text('{"task_id":"task-100","fail_reason":"QC FAIL","timestamp":"2026-04-20T10:00:00"}')

        processed = done_watcher.process_failed_files()
        assert processed == 1
        # .failed가 사라지고 .failed.acked가 생겨야 함
        assert not (tmp_path / "task-100.failed").exists()
        assert (tmp_path / "task-100.failed.acked").exists()


class TestSendTelegramNotification:
    """send_telegram_notification() 테스트"""

    def test_no_bot_token(self, monkeypatch):
        """bot token 없으면 False 반환"""
        monkeypatch.setattr(done_watcher, "DONE_PROTOCOL_LOG", Path("/tmp/test-done-protocol.log"))
        monkeypatch.delenv("ANU_BOT_TOKEN", raising=False)
        result = done_watcher.send_telegram_notification("test message")
        assert result is False


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
