# Copyright (c) 2026 동일비전(Dongil Vision Korea). All Rights Reserved.
"""
╔══════════════════════════════════════════════════════════════════════════════╗
║  ALASKA v2.0 - Task Profiler                                                 ║
║  코드 블록 프로파일링 Context Manager                                        ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Version : 2.0.0                                                             ║
║  Date    : 2026-02-10                                                        ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Summary:                                                                    ║
║    간단한 코드 블록 프로파일링을 위한 Context Manager                        ║
║    - with 문으로 소요 시간 자동 측정                                         ║
║    - 중첩 프로파일링 지원                                                    ║
║    - lap() 메서드로 중간 지점 기록                                           ║
║    - JSON export 옵션                                                        ║
╚══════════════════════════════════════════════════════════════════════════════╝
"""

from __future__ import annotations
import time
import json
from datetime import datetime
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field


@dataclass
class LapRecord:
    """lap() 호출 기록"""
    name: str
    timestamp: float
    elapsed_from_start: float
    elapsed_from_prev: float


class TaskProfiler:
    """코드 블록 프로파일링 Context Manager

    Usage:
        with task_profiler("my_operation") as profiler:
            # 프로파일링할 코드
            result = some_computation()

        # 자동으로 소요 시간 출력

    Attributes:
        name: 프로파일러 이름
        export_json: True면 종료 시 JSON 파일 생성
        silent: True면 출력 안 함
    """

    _stack: List['TaskProfiler'] = []  # 중첩 프로파일링 스택

    def __init__(
        self,
        name: str = "default",
        export_json: bool = False,
        silent: bool = False
    ):
        self.name = name
        self.export_json = export_json
        self.silent = silent

        self._start_time: float = 0.0
        self._end_time: float = 0.0
        self._laps: List[LapRecord] = []
        self._prev_lap_time: float = 0.0
        self._depth: int = 0

    @property
    def elapsed(self) -> float:
        """총 소요 시간 (초)"""
        if self._end_time > 0:
            return self._end_time - self._start_time
        return time.perf_counter() - self._start_time

    @property
    def elapsed_ms(self) -> float:
        """총 소요 시간 (밀리초)"""
        return self.elapsed * 1000

    @property
    def laps(self) -> List[LapRecord]:
        """lap 기록 목록"""
        return self._laps.copy()

    def lap(self, name: str = "") -> float:
        """중간 지점 기록

        Args:
            name: lap 이름 (미지정 시 자동 번호)

        Returns:
            이전 lap으로부터 경과 시간 (초)
        """
        now = time.perf_counter()
        elapsed_from_start = now - self._start_time
        elapsed_from_prev = now - self._prev_lap_time

        lap_name = name or f"lap_{len(self._laps) + 1}"
        record = LapRecord(
            name=lap_name,
            timestamp=now,
            elapsed_from_start=elapsed_from_start,
            elapsed_from_prev=elapsed_from_prev
        )
        self._laps.append(record)
        self._prev_lap_time = now

        if not self.silent:
            indent = "  " * self._depth
            print(f"{indent}  [{self.name}:{lap_name}] +{elapsed_from_prev:.3f}s (total: {elapsed_from_start:.3f}s)")

        return elapsed_from_prev

    def to_dict(self) -> Dict[str, Any]:
        """JSON 직렬화용 딕셔너리"""
        return {
            "name": self.name,
            "start_time": datetime.fromtimestamp(self._start_time).isoformat() if self._start_time else None,
            "end_time": datetime.fromtimestamp(self._end_time).isoformat() if self._end_time else None,
            "elapsed_seconds": round(self.elapsed, 6),
            "elapsed_ms": round(self.elapsed_ms, 3),
            "laps": [
                {
                    "name": lap.name,
                    "elapsed_from_start": round(lap.elapsed_from_start, 6),
                    "elapsed_from_prev": round(lap.elapsed_from_prev, 6),
                }
                for lap in self._laps
            ]
        }

    def _export_json(self) -> str:
        """JSON 파일로 export"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"profiler_{self.name}_{timestamp}.json"

        with open(filename, "w", encoding="utf-8") as f:
            json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)

        return filename

    def __enter__(self) -> 'TaskProfiler':
        self._depth = len(TaskProfiler._stack)
        TaskProfiler._stack.append(self)

        self._start_time = time.perf_counter()
        self._prev_lap_time = self._start_time

        if not self.silent:
            indent = "  " * self._depth
            print(f"{indent}[{self.name}] Started")

        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        self._end_time = time.perf_counter()
        TaskProfiler._stack.pop()

        if not self.silent:
            indent = "  " * self._depth
            status = "Completed" if exc_type is None else f"Failed ({exc_type.__name__})"
            print(f"{indent}[{self.name}] {status}: {self.elapsed:.3f}s")

        if self.export_json:
            filename = self._export_json()
            if not self.silent:
                indent = "  " * self._depth
                print(f"{indent}[{self.name}] Exported to: {filename}")


def task_profiler(
    name: str = "default",
    export_json: bool = False,
    silent: bool = False
) -> TaskProfiler:
    """task_profiler context manager 생성 함수

    Args:
        name: 프로파일러 이름
        export_json: True면 종료 시 JSON 파일 생성
        silent: True면 출력 안 함

    Returns:
        TaskProfiler 인스턴스

    Usage:
        with task_profiler("my_op") as profiler:
            do_something()
            profiler.lap("checkpoint")
            do_another()
    """
    return TaskProfiler(name=name, export_json=export_json, silent=silent)
