#!/usr/bin/env python3
"""utils/persistent_shell.py 테스트 스위트 (TDD)"""

import os
import sys
import time
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from utils.persistent_shell import PersistentShell


# ---------------------------------------------------------------------------
# 기본 명령 실행
# ---------------------------------------------------------------------------


class TestBasicCommands:
    """단순 명령 실행 테스트"""

    def test_echo_returns_output(self):
        """echo 명령 실행 — 출력 반환"""
        with PersistentShell() as sh:
            out, code = sh.run("echo hello")
            assert "hello" in out
            assert code == 0

    def test_exit_code_success(self):
        """성공 명령의 exit code = 0"""
        with PersistentShell() as sh:
            _, code = sh.run("true")
            assert code == 0

    def test_exit_code_failure(self):
        """실패 명령의 exit code != 0"""
        with PersistentShell() as sh:
            _, code = sh.run("false")
            assert code != 0

    def test_exit_code_specific_value(self):
        """특정 exit code 값 확인"""
        with PersistentShell() as sh:
            _, code = sh.run("exit 42", timeout=5.0)
            # 셸이 종료되므로 코드 반환 확인만
            assert code == 42

    def test_multiline_output(self):
        """여러 줄 출력 반환"""
        with PersistentShell() as sh:
            out, code = sh.run("printf 'line1\\nline2\\nline3\\n'")
            assert "line1" in out
            assert "line2" in out
            assert "line3" in out
            assert code == 0

    def test_command_with_arguments(self):
        """인자가 있는 명령 실행"""
        with PersistentShell() as sh:
            out, code = sh.run("echo -n test_value")
            assert "test_value" in out
            assert code == 0


# ---------------------------------------------------------------------------
# 디렉토리 상태 유지
# ---------------------------------------------------------------------------


class TestDirectoryPersistence:
    """cd 후 디렉토리 상태 유지 테스트"""

    def test_cd_changes_directory(self, tmp_path):
        """cd 명령으로 디렉토리 변경"""
        with PersistentShell() as sh:
            sh.run(f"cd {tmp_path}")
            out, code = sh.run("pwd")
            assert str(tmp_path) in out
            assert code == 0

    def test_cd_state_persists_across_commands(self, tmp_path):
        """cd 후 다음 명령에도 디렉토리 유지"""
        with PersistentShell() as sh:
            sh.run(f"cd {tmp_path}")
            sh.run("true")  # 중간 명령
            out, _ = sh.run("pwd")
            assert str(tmp_path) in out

    def test_get_cwd_reflects_cd(self, tmp_path):
        """get_cwd()가 cd 이후 디렉토리 반환"""
        with PersistentShell() as sh:
            sh.run(f"cd {tmp_path}")
            cwd = sh.get_cwd()
            assert str(tmp_path) in cwd

    def test_multiple_cd_commands(self, tmp_path):
        """연속 cd 명령으로 디렉토리 추적"""
        subdir = tmp_path / "subdir"
        subdir.mkdir()
        with PersistentShell() as sh:
            sh.run(f"cd {tmp_path}")
            sh.run("cd subdir")
            cwd = sh.get_cwd()
            assert "subdir" in cwd


# ---------------------------------------------------------------------------
# 환경변수 상태 유지
# ---------------------------------------------------------------------------


class TestEnvironmentPersistence:
    """환경변수 설정 및 조회 테스트"""

    def test_set_and_get_env_var(self):
        """환경변수 설정 후 조회"""
        with PersistentShell() as sh:
            sh.run("export MY_VAR=hello_world")
            val = sh.get_env("MY_VAR")
            assert val == "hello_world"

    def test_env_var_persists_across_commands(self):
        """환경변수가 여러 명령에 걸쳐 유지"""
        with PersistentShell() as sh:
            sh.run("export PERSIST_VAR=persistent")
            sh.run("true")  # 중간 명령
            out, _ = sh.run("echo $PERSIST_VAR")
            assert "persistent" in out

    def test_get_env_returns_none_for_missing(self):
        """존재하지 않는 환경변수 → None"""
        with PersistentShell() as sh:
            val = sh.get_env("__NONEXISTENT_VAR_12345__")
            assert val is None

    def test_env_var_override(self):
        """환경변수 덮어쓰기"""
        with PersistentShell() as sh:
            sh.run("export OVERRIDE_VAR=first")
            sh.run("export OVERRIDE_VAR=second")
            val = sh.get_env("OVERRIDE_VAR")
            assert val == "second"


# ---------------------------------------------------------------------------
# 컨텍스트 매니저
# ---------------------------------------------------------------------------


class TestContextManager:
    """컨텍스트 매니저 사용 테스트"""

    def test_context_manager_enters_and_exits(self):
        """with 블록 정상 진입/종료"""
        with PersistentShell() as sh:
            out, code = sh.run("echo context_ok")
            assert "context_ok" in out
            assert code == 0

    def test_context_manager_cleans_up_on_exit(self):
        """with 블록 종료 후 프로세스 정리"""
        with PersistentShell() as sh:
            proc = sh
        # 종료 후 run 호출 시 예외 발생하거나 빈 결과
        # 중요한 것은 블록이 정상 종료됐다는 것
        assert proc is not None

    def test_explicit_close(self):
        """명시적 close() 호출"""
        sh = PersistentShell()
        sh.run("echo before_close")
        sh.close()
        # close 이후 재호출해도 크래시 없음
        sh.close()


# ---------------------------------------------------------------------------
# 타임아웃
# ---------------------------------------------------------------------------


class TestTimeout:
    """타임아웃 처리 테스트"""

    def test_timeout_raises_or_returns_on_long_command(self):
        """긴 명령에 타임아웃 발생"""
        with PersistentShell() as sh:
            start = time.time()
            try:
                out, code = sh.run("sleep 60", timeout=1.0)
                # 타임아웃 시 빠르게 반환돼야 함
                elapsed = time.time() - start
                assert elapsed < 10.0
            except Exception:
                elapsed = time.time() - start
                assert elapsed < 10.0

    def test_fast_command_no_timeout(self):
        """빠른 명령은 타임아웃 없이 완료"""
        with PersistentShell() as sh:
            out, code = sh.run("echo fast", timeout=5.0)
            assert "fast" in out
            assert code == 0


# ---------------------------------------------------------------------------
# 연속 명령 상태 유지
# ---------------------------------------------------------------------------


class TestSequentialState:
    """연속 명령 실행 상태 유지 테스트"""

    def test_sequential_commands_maintain_state(self, tmp_path):
        """여러 명령 연속 실행 시 상태 유지"""
        with PersistentShell() as sh:
            sh.run(f"cd {tmp_path}")
            sh.run("export SEQ_VAR=sequential")
            out_pwd, _ = sh.run("pwd")
            out_env, _ = sh.run("echo $SEQ_VAR")
            assert str(tmp_path) in out_pwd
            assert "sequential" in out_env

    def test_run_returns_tuple(self):
        """run()이 (str, int) 튜플 반환"""
        with PersistentShell() as sh:
            result = sh.run("echo hello")
            assert isinstance(result, tuple)
            assert len(result) == 2
            assert isinstance(result[0], str)
            assert isinstance(result[1], int)

    def test_file_created_in_persistent_dir(self, tmp_path):
        """cd 후 파일 생성 — 실제 해당 디렉토리에 생성됨"""
        with PersistentShell() as sh:
            sh.run(f"cd {tmp_path}")
            sh.run("echo content > testfile.txt")
            assert (tmp_path / "testfile.txt").exists()


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