#!/usr/bin/env python3
"""파일 크기 체커: Read tool 토큰 리밋(10000) 초과 위험 파일 탐지

사용법:
  python3 file_size_checker.py check <파일경로>          # 단일 파일 체크
  python3 file_size_checker.py scan <디렉토리>           # 디렉토리 스캔
  python3 file_size_checker.py guide <파일경로>           # 큰 파일에 대한 offset/limit 가이드 출력
"""

import argparse
import json
import sys
from pathlib import Path
from typing import Dict, List, Any

TOKEN_LIMIT = 10000
LINES_PER_TOKEN = 15  # 보수적 추정: 1줄 ≈ 15 토큰
SCAN_EXTENSIONS = {".md", ".py", ".json", ".yaml", ".yml"}

# 확장자별 bytes/token 추정
BYTES_PER_TOKEN: Dict[str, float] = {
    ".md": 3.0,    # 한글 위주
    ".py": 4.0,    # 영어 위주
    ".json": 4.0,
    ".yaml": 4.0,
    ".yml": 4.0,
}
DEFAULT_BYTES_PER_TOKEN = 4.0


def estimate_tokens(size_bytes: int, extension: str) -> int:
    """파일 크기(bytes)와 확장자로 토큰 수를 추정합니다."""
    bpt = BYTES_PER_TOKEN.get(extension.lower(), DEFAULT_BYTES_PER_TOKEN)
    return int(size_bytes / bpt)


def cmd_check(file_path: str) -> None:
    """단일 파일의 크기를 체크하고 토큰 초과 여부를 JSON으로 출력합니다."""
    path = Path(file_path)
    if not path.is_file():
        print(json.dumps({"error": f"파일을 찾을 수 없습니다: {file_path}"}))
        sys.exit(1)

    size_bytes = path.stat().st_size
    extension = path.suffix
    estimated_tokens = estimate_tokens(size_bytes, extension)
    exceeds_limit = estimated_tokens > TOKEN_LIMIT

    result: Dict[str, Any] = {
        "path": str(path.resolve()),
        "size_bytes": size_bytes,
        "estimated_tokens": estimated_tokens,
        "exceeds_limit": exceeds_limit,
        "limit": TOKEN_LIMIT,
    }
    print(json.dumps(result, ensure_ascii=False, indent=2))


def cmd_scan(directory: str) -> None:
    """디렉토리를 스캔하여 초과 위험 파일 목록을 JSON으로 출력합니다."""
    dir_path = Path(directory)
    if not dir_path.is_dir():
        print(json.dumps({"error": f"디렉토리를 찾을 수 없습니다: {directory}"}))
        sys.exit(1)

    files: List[Dict[str, Any]] = []
    total_scanned = 0
    exceeds_count = 0

    for file_path in dir_path.rglob("*"):
        if not file_path.is_file():
            continue
        if file_path.suffix.lower() not in SCAN_EXTENSIONS:
            continue

        total_scanned += 1
        size_bytes = file_path.stat().st_size
        extension = file_path.suffix
        estimated_tokens = estimate_tokens(size_bytes, extension)
        exceeds_limit = estimated_tokens > TOKEN_LIMIT

        if exceeds_limit:
            exceeds_count += 1

        files.append({
            "path": str(file_path.resolve()),
            "size_bytes": size_bytes,
            "estimated_tokens": estimated_tokens,
            "exceeds_limit": exceeds_limit,
            "limit": TOKEN_LIMIT,
        })

    # 추정 토큰 수 내림차순 정렬
    files.sort(key=lambda f: f["estimated_tokens"], reverse=True)

    result: Dict[str, Any] = {
        "files": files,
        "total_scanned": total_scanned,
        "exceeds_count": exceeds_count,
    }
    print(json.dumps(result, ensure_ascii=False, indent=2))


def cmd_guide(file_path: str) -> None:
    """큰 파일을 Read tool로 읽을 때 필요한 offset/limit 값 안내를 출력합니다."""
    path = Path(file_path)
    if not path.is_file():
        print(f"오류: 파일을 찾을 수 없습니다: {file_path}", file=sys.stderr)
        sys.exit(1)

    # 전체 줄 수 파악
    try:
        with open(path, "r", encoding="utf-8", errors="replace") as f:
            lines = f.readlines()
    except Exception as e:
        print(f"오류: 파일을 읽을 수 없습니다: {e}", file=sys.stderr)
        sys.exit(1)

    total_lines = len(lines)
    # 1줄 ≈ 15 토큰 (보수적 추정)
    total_estimated_tokens = total_lines * LINES_PER_TOKEN

    # 10000 토큰에 해당하는 줄 수
    lines_per_chunk = TOKEN_LIMIT // LINES_PER_TOKEN  # 666줄

    print(
        f"이 파일은 {total_lines}줄, 약 {total_estimated_tokens} 토큰입니다. "
        f"Read tool로 읽으려면:"
    )

    if total_estimated_tokens <= TOKEN_LIMIT:
        print("  (전체를 한 번에 읽을 수 있습니다. offset/limit 불필요)")
        return

    part = 1
    offset = 1
    while offset <= total_lines:
        remaining = total_lines - offset + 1
        limit = min(lines_per_chunk, remaining)
        print(f"  Part {part}: offset={offset}, limit={limit}")
        offset += lines_per_chunk
        part += 1


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Read tool 토큰 리밋(10000) 초과 위험 파일 탐지 유틸리티",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__,
    )
    subparsers = parser.add_subparsers(dest="command", required=True)

    # check 서브커맨드
    check_parser = subparsers.add_parser(
        "check", help="단일 파일의 토큰 초과 여부를 체크합니다"
    )
    check_parser.add_argument("file_path", type=str, help="체크할 파일 경로")

    # scan 서브커맨드
    scan_parser = subparsers.add_parser(
        "scan", help="디렉토리 전체를 스캔하여 초과 위험 파일 목록을 반환합니다"
    )
    scan_parser.add_argument("directory", type=str, help="스캔할 디렉토리 경로")

    # guide 서브커맨드
    guide_parser = subparsers.add_parser(
        "guide", help="큰 파일에 대한 offset/limit 읽기 가이드를 출력합니다"
    )
    guide_parser.add_argument("file_path", type=str, help="가이드를 출력할 파일 경로")

    args = parser.parse_args()

    if args.command == "check":
        cmd_check(args.file_path)
    elif args.command == "scan":
        cmd_scan(args.directory)
    elif args.command == "guide":
        cmd_guide(args.file_path)


if __name__ == "__main__":
    main()
