"""
╔══════════════════════════════════════════════════════════════════════════════╗
║  ALASKA v2.0 - Task Performance                                              ║
║  RMI 성능 측정 및 통계 관리 모듈                                             ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Version : 2.0.1                                                             ║
║  Date    : 2026-02-07                                                        ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Summary:                                                                    ║
║    RMI 호출 성능 측정 및 통계 관리 시스템                                    ║
║    - IPC/FUNC 시간 측정 (min/avg/max)                                        ║
║    - Task/메서드별 성능 집계                                                 ║
║    - 로컬 캐시 + 배치 플러시로 IPC 오버헤드 최소화                           ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Classes:                                                                    ║
║    MethodPerformance   - 메서드별 성능 통계                                  ║
║    TaskPerformance     - Task별 성능 집계                                    ║
║    SystemPerformance   - 시스템 전체 통계                                    ║
║    TimingRecorder      - 로컬 타이밍 기록 및 배치 플러시                     ║
║    PerformanceCollector- 시스템 통계 수집기                                  ║
╚══════════════════════════════════════════════════════════════════════════════╝
"""

from __future__ import annotations
from dataclasses import dataclass, field
from typing import Dict, List, Any, Optional, Tuple, TYPE_CHECKING, ClassVar
import time

if TYPE_CHECKING:
    from .task_manager import TaskInfo


# ═══════════════════════════════════════════════════════════════════════════════
# 데이터 클래스 (PERF-003, PERF-002, PERF-001)
# ═══════════════════════════════════════════════════════════════════════════════

@dataclass
class MethodPerformance:
    """PERF-003: 메서드별 성능 통계

    각 RMI 메서드의 호출 횟수와 IPC/FUNC 시간 통계를 관리합니다.
    최근 WINDOW_SIZE(10)개의 측정값을 기반으로 min/avg/max를 계산합니다.

    Attributes:
        name: 메서드 이름
        call_count: 호출 횟수
        ipc_window: IPC 시간 윈도우 (최근 10개, ms)
        func_window: FUNC 시간 윈도우 (최근 10개, ms)
    """
    name: str
    call_count: int = 0
    ipc_window: List[float] = field(default_factory=list)
    func_window: List[float] = field(default_factory=list)

    WINDOW_SIZE: ClassVar[int] = 10

    @property
    def ipc_stats(self) -> Dict[str, float]:
        """IPC 시간 통계 (min/avg/max in ms)"""
        if not self.ipc_window:
            return {'min': 0.0, 'avg': 0.0, 'max': 0.0}
        return {
            'min': round(min(self.ipc_window), 2),
            'avg': round(sum(self.ipc_window) / len(self.ipc_window), 2),
            'max': round(max(self.ipc_window), 2)
        }

    @property
    def func_stats(self) -> Dict[str, float]:
        """FUNC 시간 통계 (min/avg/max in ms)"""
        if not self.func_window:
            return {'min': 0.0, 'avg': 0.0, 'max': 0.0}
        return {
            'min': round(min(self.func_window), 2),
            'avg': round(sum(self.func_window) / len(self.func_window), 2),
            'max': round(max(self.func_window), 2)
        }

    @property
    def total_time_avg(self) -> float:
        """평균 총 소요 시간 (IPC + FUNC)"""
        return round(self.ipc_stats['avg'] + self.func_stats['avg'], 2)

    def to_dict(self) -> Dict[str, Any]:
        """JSON 직렬화용 딕셔너리 변환"""
        return {
            'name': self.name,
            'count': self.call_count,
            'ipc': self.ipc_stats,
            'func': self.func_stats,
            'total_avg': self.total_time_avg,
        }


@dataclass
class TaskPerformance:
    """PERF-002: Task별 성능 통계

    개별 Task(프로세스/스레드)의 성능 통계를 집계합니다.

    Attributes:
        task_id: Task 식별자
        task_class: Task 클래스명
        mode: 실행 모드 ('thread' | 'process')
        alive: Task 활성 여부
        methods: 메서드별 성능 데이터
    """
    task_id: str
    task_class: str = ""
    mode: str = "thread"
    alive: bool = False
    methods: Dict[str, MethodPerformance] = field(default_factory=dict)

    @property
    def total_calls(self) -> int:
        """전체 메서드 호출 수"""
        return sum(m.call_count for m in self.methods.values())

    @property
    def method_count(self) -> int:
        """등록된 메서드 수"""
        return len(self.methods)

    @property
    def avg_ipc_time(self) -> float:
        """평균 IPC 시간 (모든 메서드의 윈도우 평균)"""
        all_ipc = []
        for m in self.methods.values():
            all_ipc.extend(m.ipc_window)
        return round(sum(all_ipc) / len(all_ipc), 2) if all_ipc else 0.0

    @property
    def avg_func_time(self) -> float:
        """평균 FUNC 시간 (모든 메서드의 윈도우 평균)"""
        all_func = []
        for m in self.methods.values():
            all_func.extend(m.func_window)
        return round(sum(all_func) / len(all_func), 2) if all_func else 0.0

    @property
    def avg_total_time(self) -> float:
        """평균 총 소요 시간 (IPC + FUNC)"""
        return round(self.avg_ipc_time + self.avg_func_time, 2)

    def to_dict(self) -> Dict[str, Any]:
        """JSON 직렬화용 딕셔너리 변환"""
        return {
            'task_id': self.task_id,
            'task_class': self.task_class,
            'mode': self.mode,
            'alive': self.alive,
            'total_calls': self.total_calls,
            'method_count': self.method_count,
            'avg_ipc_time': self.avg_ipc_time,
            'avg_func_time': self.avg_func_time,
            'avg_total_time': self.avg_total_time,
            'methods': {k: v.to_dict() for k, v in self.methods.items()}
        }


@dataclass
class SystemPerformance:
    """PERF-001: 시스템 전체 성능 통계

    모든 Task의 성능 데이터를 집계하여 시스템 수준 통계를 제공합니다.

    Attributes:
        tasks: Task별 성능 데이터
        measurement_enabled: 측정 활성화 여부
        start_time: 측정 시작 시간 (Unix timestamp)
    """
    tasks: Dict[str, TaskPerformance] = field(default_factory=dict)
    measurement_enabled: bool = False
    start_time: float = field(default_factory=time.time)

    @property
    def total_calls(self) -> int:
        """시스템 전체 RMI 호출 수"""
        return sum(t.total_calls for t in self.tasks.values())

    @property
    def total_tasks(self) -> int:
        """등록된 Task 수"""
        return len(self.tasks)

    @property
    def active_tasks(self) -> int:
        """활성 Task 수 (alive=True)"""
        return sum(1 for t in self.tasks.values() if t.alive)

    @property
    def total_methods(self) -> int:
        """전체 메서드 수"""
        return sum(t.method_count for t in self.tasks.values())

    @property
    def avg_ipc_time(self) -> float:
        """시스템 평균 IPC 시간"""
        all_ipc = []
        for t in self.tasks.values():
            for m in t.methods.values():
                all_ipc.extend(m.ipc_window)
        return round(sum(all_ipc) / len(all_ipc), 2) if all_ipc else 0.0

    @property
    def avg_func_time(self) -> float:
        """시스템 평균 FUNC 시간"""
        all_func = []
        for t in self.tasks.values():
            for m in t.methods.values():
                all_func.extend(m.func_window)
        return round(sum(all_func) / len(all_func), 2) if all_func else 0.0

    @property
    def avg_total_time(self) -> float:
        """시스템 평균 총 소요 시간"""
        return round(self.avg_ipc_time + self.avg_func_time, 2)

    @property
    def uptime_seconds(self) -> int:
        """측정 시작 후 경과 시간 (초)"""
        return int(time.time() - self.start_time)

    def to_dict(self) -> Dict[str, Any]:
        """JSON 직렬화용 딕셔너리 변환"""
        return {
            'total_calls': self.total_calls,
            'total_tasks': self.total_tasks,
            'active_tasks': self.active_tasks,
            'total_methods': self.total_methods,
            'avg_ipc_time': self.avg_ipc_time,
            'avg_func_time': self.avg_func_time,
            'avg_total_time': self.avg_total_time,
            'uptime_seconds': self.uptime_seconds,
            'measurement_enabled': self.measurement_enabled,
            'tasks': {k: v.to_dict() for k, v in self.tasks.items()}
        }


# ═══════════════════════════════════════════════════════════════════════════════
# TimingRecorder (PERF-003, PERF-007: 기존 로직 이관)
# ═══════════════════════════════════════════════════════════════════════════════

class TimingRecorder:
    """TaskWorker에서 사용하는 로컬 타이밍 기록기

    기존 TaskWorker의 _local_methods, _local_timing,
    _record_timing, _flush_* 메서드를 캡슐화합니다.

    로컬 캐시를 사용하여 IPC 오버헤드를 줄이고,
    주기적으로 공유 메모리로 배치 플러시합니다.

    Thread-safe: 단일 워커 내에서만 사용 (IPC 없음)

    Attributes:
        WINDOW_SIZE: 타이밍 윈도우 크기 (기본 10)
        FLUSH_INTERVAL: 자동 플러시 간격 (기본 50회)
    """

    WINDOW_SIZE: int = 10
    FLUSH_INTERVAL: int = 50

    __slots__ = ('_local_methods', '_local_timing', '_flush_count',
                 '_rmi_methods', '_rmi_timing')

    def __init__(self, rmi_methods, rmi_timing):
        """
        Args:
            rmi_methods: Manager.dict() - 공유 메서드 카운터
            rmi_timing: Manager.dict() - 공유 타이밍 데이터
        """
        self._local_methods: Dict[str, int] = {}
        self._local_timing: Dict[str, Tuple[List[float], List[float]]] = {}
        self._flush_count: int = 0
        self._rmi_methods = rmi_methods
        self._rmi_timing = rmi_timing

    def record(self, method: str, ipc_ms: float, func_ms: float) -> None:
        """타이밍 데이터 기록 (로컬 캐시)

        기존 TaskWorker._record_timing() 이관

        Args:
            method: 메서드 이름
            ipc_ms: IPC 소요 시간 (ms)
            func_ms: FUNC 소요 시간 (ms)
        """
        # 메서드 호출 수 증가
        self._local_methods[method] = self._local_methods.get(method, 0) + 1

        # 타이밍 데이터 기록
        if method not in self._local_timing:
            self._local_timing[method] = ([], [])
        ipc_list, func_list = self._local_timing[method]
        ipc_list.append(ipc_ms)
        func_list.append(func_ms)

        # 로컬 캐시 크기 제한
        if len(ipc_list) > self.WINDOW_SIZE:
            self._local_timing[method] = (
                ipc_list[-self.WINDOW_SIZE:],
                func_list[-self.WINDOW_SIZE:]
            )

        # 주기적 플러시
        self._flush_count += 1
        if self._flush_count >= self.FLUSH_INTERVAL:
            self.flush()
            self._flush_count = 0

    def record_count_only(self, method: str) -> None:
        """호출 횟수만 기록 (타이밍 없음)

        t0가 없는 경우 (one-way 호출 등)에 사용

        Args:
            method: 메서드 이름
        """
        self._local_methods[method] = self._local_methods.get(method, 0) + 1
        self._flush_count += 1
        if self._flush_count >= self.FLUSH_INTERVAL:
            self.flush()
            self._flush_count = 0

    def flush(self) -> None:
        """로컬 데이터를 공유 메모리로 플러시

        기존 TaskWorker._flush_method_counts(), _flush_timing() 통합
        """
        # 메서드 카운터 플러시
        if self._local_methods:
            for m, cnt in self._local_methods.items():
                self._rmi_methods[m] = self._rmi_methods.get(m, 0) + cnt
            self._local_methods.clear()

        # 타이밍 데이터 플러시
        if self._local_timing:
            for m, (ipc_list, func_list) in self._local_timing.items():
                existing = self._rmi_timing.get(m)
                if existing:
                    ex_ipc, ex_func = existing
                    merged_ipc = (list(ex_ipc) + ipc_list)[-self.WINDOW_SIZE:]
                    merged_func = (list(ex_func) + func_list)[-self.WINDOW_SIZE:]
                    self._rmi_timing[m] = (merged_ipc, merged_func)
                else:
                    self._rmi_timing[m] = (
                        ipc_list[-self.WINDOW_SIZE:],
                        func_list[-self.WINDOW_SIZE:]
                    )
            self._local_timing.clear()


# ═══════════════════════════════════════════════════════════════════════════════
# PerformanceCollector (PERF-001, PERF-002, PERF-006)
# ═══════════════════════════════════════════════════════════════════════════════

class PerformanceCollector:
    """시스템 성능 데이터 수집기

    TaskManager에서 생성하여 모든 Task의 성능 데이터를 집계합니다.
    TaskMonitor가 이 객체를 통해 API 응답을 생성합니다.

    3계층 성능 데이터 접근:
    - 시스템 레벨: get_system_performance()
    - Task 레벨: get_task_performance(task_id)
    - 메서드 레벨: get_method_performance(task_id, method)

    Process-safe: Manager 객체 기반 공유 데이터 사용

    Attributes:
        _tasks: TaskManager._tasks 참조
        _workers: TaskManager._workers 참조 (alive 상태 확인용)
        _measurement: 측정 ON/OFF 공유 변수
        _start_time: 수집기 시작 시간
    """

    __slots__ = ('_tasks', '_workers', '_measurement', '_start_time')

    def __init__(self, tasks: Dict[str, 'TaskInfo'], workers: dict, measurement):
        """
        Args:
            tasks: TaskManager._tasks 참조
            workers: TaskManager._workers 참조
            measurement: Manager.Value('b', bool) - 측정 ON/OFF
        """
        self._tasks = tasks
        self._workers = workers
        self._measurement = measurement
        self._start_time = time.time()

    # ─────────────────────────────────────────────────────────────────────────
    # PERF-001: 시스템 레벨 API
    # ─────────────────────────────────────────────────────────────────────────

    def get_system_performance(self) -> SystemPerformance:
        """시스템 전체 성능 통계 반환

        Returns:
            SystemPerformance 객체
        """
        sys_perf = SystemPerformance(
            measurement_enabled=self._measurement.value if self._measurement else False,
            start_time=self._start_time
        )

        for tid in self._tasks.keys():
            task_perf = self.get_task_performance(tid)
            if task_perf:
                sys_perf.tasks[tid] = task_perf

        return sys_perf

    # ─────────────────────────────────────────────────────────────────────────
    # PERF-002: Task 레벨 API
    # ─────────────────────────────────────────────────────────────────────────

    def get_task_performance(self, task_id: str) -> Optional[TaskPerformance]:
        """특정 Task의 성능 통계 반환

        Args:
            task_id: Task 식별자

        Returns:
            TaskPerformance 객체 또는 None
        """
        ti = self._tasks.get(task_id)
        if not ti:
            return None

        # alive 상태 확인
        worker = self._workers.get(task_id) if self._workers else None
        alive = worker.is_alive() if worker else False

        task_perf = TaskPerformance(
            task_id=task_id,
            task_class=ti.task_class_name,
            mode=ti.mode,
            alive=alive
        )

        # 메서드별 데이터 수집
        methods = dict(ti.rmi_methods) if ti.rmi_methods else {}
        timing = dict(ti.rmi_timing) if ti.rmi_timing else {}

        for method_name, count in methods.items():
            method_perf = MethodPerformance(name=method_name, call_count=count)
            t = timing.get(method_name)
            if t:
                ipc_list, func_list = t
                method_perf.ipc_window = list(ipc_list) if ipc_list else []
                method_perf.func_window = list(func_list) if func_list else []
            task_perf.methods[method_name] = method_perf

        return task_perf

    def get_all_tasks_performance(self) -> List[TaskPerformance]:
        """모든 Task의 성능 통계 반환

        Returns:
            TaskPerformance 리스트
        """
        result = []
        for tid in self._tasks.keys():
            perf = self.get_task_performance(tid)
            if perf:
                result.append(perf)
        return result

    # ─────────────────────────────────────────────────────────────────────────
    # PERF-003: 메서드 레벨 API
    # ─────────────────────────────────────────────────────────────────────────

    def get_method_performance(self, task_id: str, method_name: str) -> Optional[MethodPerformance]:
        """특정 메서드의 성능 통계 반환

        Args:
            task_id: Task 식별자
            method_name: 메서드명

        Returns:
            MethodPerformance 객체 또는 None
        """
        task_perf = self.get_task_performance(task_id)
        if not task_perf:
            return None
        return task_perf.methods.get(method_name)

    def get_task_methods_performance(self, task_id: str) -> List[MethodPerformance]:
        """특정 Task의 모든 메서드 성능 통계 반환

        Args:
            task_id: Task 식별자

        Returns:
            MethodPerformance 리스트 (호출 수 내림차순 정렬)
        """
        task_perf = self.get_task_performance(task_id)
        if not task_perf:
            return []
        # 호출 수 기준 내림차순 정렬
        return sorted(task_perf.methods.values(), key=lambda m: m.call_count, reverse=True)

    # ─────────────────────────────────────────────────────────────────────────
    # PERF-009, PERF-010: 제어 API
    # ─────────────────────────────────────────────────────────────────────────

    def set_measurement_enabled(self, enabled: bool) -> None:
        """측정 ON/OFF 설정

        Args:
            enabled: True=측정 활성화, False=비활성화
        """
        if self._measurement:
            self._measurement.value = enabled

    def is_measurement_enabled(self) -> bool:
        """측정 활성화 여부 반환"""
        return self._measurement.value if self._measurement else False

    def toggle_measurement(self) -> bool:
        """측정 ON/OFF 토글

        Returns:
            토글 후 상태
        """
        if self._measurement:
            self._measurement.value = not self._measurement.value
            return self._measurement.value
        return False

    def clear_all(self) -> None:
        """모든 성능 데이터 초기화"""
        for ti in self._tasks.values():
            if ti.rmi_methods:
                ti.rmi_methods.clear()
            if ti.rmi_timing:
                ti.rmi_timing.clear()
        self._start_time = time.time()

    def clear_task(self, task_id: str) -> bool:
        """특정 Task의 성능 데이터 초기화

        Args:
            task_id: Task 식별자

        Returns:
            성공 여부
        """
        ti = self._tasks.get(task_id)
        if not ti:
            return False
        if ti.rmi_methods:
            ti.rmi_methods.clear()
        if ti.rmi_timing:
            ti.rmi_timing.clear()
        return True
