"""event_bus.py — 원자적 이벤트 소비 모듈.

POSIX rename()의 원자성을 이용해 동일 파일시스템 내에서
두 프로세스가 동시에 같은 이벤트를 소비하려 해도 한 쪽만 성공하도록 보장한다.

작성자 : 토르 (dev2-team backend)
날짜   : 2026-03-24
"""

import logging
import os
import shutil

__all__ = ["consume_event", "scan_done_events"]

logger = logging.getLogger(__name__)


def consume_event(incoming_dir: str, processed_dir: str, event_file: str) -> bool:
    """원자적으로 이벤트를 소비한다. 성공 시 True, 이미 소비됨/거부 시 False.

    Args:
        incoming_dir: 미처리 이벤트 파일이 위치한 디렉터리 경로.
        processed_dir: 처리 완료된 이벤트 파일을 이동할 디렉터리 경로.
        event_file: 이벤트 파일 이름 (디렉터리 구분자 없는 순수 파일명).

    Returns:
        True  — 이 프로세스가 이벤트를 성공적으로 소비함.
        False — 이미 다른 프로세스가 선점했거나, 보안 거부(symlink), 기타 오류.
    """
    src_path = os.path.join(incoming_dir, event_file)
    dst_path = os.path.join(processed_dir, event_file)

    # 보안 검증: symlink이면 즉시 거부
    if os.path.islink(src_path):
        logger.warning("보안 거부: '%s' 는 symlink입니다. 이벤트 소비를 건너뜁니다.", src_path)
        return False

    try:
        # POSIX rename()은 같은 파일시스템 내에서 원자적으로 동작하므로
        # 두 프로세스가 동시에 호출해도 정확히 한 쪽만 성공한다.
        os.rename(src_path, dst_path)
        return True
    except FileNotFoundError:
        # 다른 프로세스가 먼저 이벤트를 소비한 정상적인 선점 케이스
        logger.info(
            "이벤트 파일 '%s' 을 찾을 수 없습니다. 다른 프로세스가 이미 소비했습니다.",
            src_path,
        )
        return False
    except OSError as exc:
        logger.error("이벤트 소비 중 OS 오류 발생 (파일: '%s'): %s", src_path, exc)
        return False


def scan_done_events(events_dir: str, incoming_dir: str, processed_dir: str) -> list[str]:
    """memory/events/*.done 파일을 스캔하여 incoming/으로 복사한다.

    Layer 2↔3 통합의 핵심: .done 파일 원본을 보존하면서 incoming/으로 복사하여
    auto_orch.py가 파이프라인 트리거를 평가할 수 있게 한다.

    Args:
        events_dir: .done 파일이 생성되는 원본 디렉토리 (memory/events/)
        incoming_dir: 미처리 이벤트 복사 대상 디렉토리 (orchestrator/incoming/)
        processed_dir: 처리 완료된 이벤트 디렉토리 (orchestrator/processed/)

    Returns:
        이번 호출에서 새로 복사된 이벤트 파일명 목록
    """
    if not os.path.isdir(events_dir):
        return []

    copied: list[str] = []

    for entry in os.scandir(events_dir):
        fname = entry.name

        # *.done 패턴만 처리 (정확히 .done으로 끝나야 함)
        if not fname.endswith(".done"):
            logger.debug("패턴 불일치: '%s' 는 .done으로 끝나지 않음 — 건너뜀.", fname)
            continue

        # symlink 보안 거부
        if entry.is_symlink():
            logger.warning(
                "보안 거부: '%s' 는 symlink입니다. scan_done_events가 건너뜁니다.",
                entry.path,
            )
            continue

        # 중복 방지: incoming/ 또는 processed/에 이미 존재하면 건너뜀
        if os.path.exists(os.path.join(incoming_dir, fname)):
            logger.debug("중복 건너뜀: '%s' 가 이미 incoming/에 존재합니다.", fname)
            continue

        if os.path.exists(os.path.join(processed_dir, fname)):
            logger.debug("중복 건너뜀: '%s' 가 이미 processed/에 존재합니다.", fname)
            continue

        # 원본 보존: shutil.copy2()로 복사 (원본 삭제/이동 없음)
        dst_path = os.path.join(incoming_dir, fname)
        shutil.copy2(entry.path, dst_path)
        logger.info("이벤트 파일 '%s' 을 incoming/으로 복사했습니다.", fname)
        copied.append(fname)

    return copied
