# Copyright (c) 2026 동일비전(Dongil Vision Korea). All Rights Reserved.
"""
╔══════════════════════════════════════════════════════════════════════════════╗
║  ALASKA v2.3 - SmBlockHandler                                                ║
║  SmBlock 생성/주입 핸들러                                                    ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Version : 2.3.0                                                             ║
║  Date    : 2026-02-27                                                        ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Summary:                                                                    ║
║    config.json의 _smblock 설정으로 SmBlock 풀 생성 및 태스크 주입 관리      ║
║    TaskManager.start_all() / stop_all()에서 호출                             ║
╚══════════════════════════════════════════════════════════════════════════════╝
"""

from typing import Dict
from .sm_block import SmBlock


class SmBlockHandler:
    """SmBlock 생성/주입 핸들러. TaskManager에서 사용."""
    _pools: Dict[str, 'SmBlock'] = {}

    @classmethod
    def creation(cls, gconfig, mgr, tasks: Dict) -> Dict[str, 'SmBlock']:
        """_smblock 설정으로 SmBlock 생성 및 injection dict 변환.

        Args:
            gconfig: GConfig instance containing:
                - platform_config._smblock: (new format) Shared memory block definitions
                - task_config._smblock: (old format, fallback) Shared memory block definitions
        """
        # New format: platform_config._smblock, fallback: task_config._smblock
        platform_config = gconfig.data_get("platform_config", {})
        task_config = gconfig.data_get("task_config", {})
        smblock_cfg = platform_config.get("_smblock", {}) or task_config.get("_smblock", {})
        if not smblock_cfg:
            return cls._pools

        smblock_info = {}
        for name, cfg in smblock_cfg.items():
            if name in cls._pools:
                continue
            shape = cfg.get("shape")
            maxsize = cfg.get("maxsize", 100)
            if not shape:
                print(f"[SmBlockHandler] '{name}': shape required")
                continue
            shape = tuple(shape) if isinstance(shape, list) else shape
            lock = mgr.Lock()
            try:
                pool = SmBlock(name, shape, maxsize, create=True, lock=lock)
                cls._pools[name] = pool
                smblock_info[name] = {"name": name, "shape": shape, "maxsize": maxsize, "lock": lock}
            except FileExistsError:
                from multiprocessing.shared_memory import SharedMemory
                try:
                    old = SharedMemory(name=name, create=False)
                    old.close()
                    old.unlink()
                except Exception:
                    pass
                pool = SmBlock(name, shape, maxsize, create=True, lock=lock)
                cls._pools[name] = pool
                smblock_info[name] = {"name": name, "shape": shape, "maxsize": maxsize, "lock": lock}
            except Exception as e:
                print(f"[SmBlockHandler] '{name}' failed: {e}")

        # injection 문자열 → dict 변환
        for tid, ti in tasks.items():
            for k, v in list(ti.injection.items()):
                if isinstance(v, str) and v.startswith("smblock:"):
                    pool_name = v[8:]
                    if pool_name in smblock_info:
                        ti.injection[k] = {"_smblock": pool_name, **smblock_info[pool_name]}
                    else:
                        available = list(smblock_info.keys()) or "(none)"
                        raise ValueError(f"[SmBlockHandler] {tid}.{k}: '{pool_name}' not defined. Available: {available}")

        # 테이블 출력
        if cls._pools:
            cls._print_smblock_table()
        return cls._pools

    @classmethod
    def _print_smblock_table(cls):
        """SmBlock 정보 테이블 출력."""
        headers = ['Name', 'Shape', 'MaxSize', 'Size(MB)']
        rows = []
        for name, pool in cls._pools.items():
            shape_str = str(pool.shape)
            total_bytes = pool.index_size + (pool.item_size * pool.maxsize) + 2
            size_mb = f"{total_bytes / (1024 * 1024):.1f}"
            rows.append([name, shape_str, str(pool.maxsize), size_mb])
        widths = [max(len(h), max((len(r[i]) for r in rows), default=0)) for i, h in enumerate(headers)]
        top    = '┌' + '┬'.join('─' * (w + 2) for w in widths) + '┐'
        mid    = '├' + '┼'.join('─' * (w + 2) for w in widths) + '┤'
        bottom = '└' + '┴'.join('─' * (w + 2) for w in widths) + '┘'
        print('\n' + top)
        print('│' + '│'.join(f' {h:<{w}} ' for h, w in zip(headers, widths)) + '│')
        print(mid)
        for row in rows:
            print('│' + '│'.join(f' {c:<{w}} ' for c, w in zip(row, widths)) + '│')
        print(bottom + '\n')

    @classmethod
    def injection(cls, worker, _tasks: Dict):
        """injection dict를 SmBlock 인스턴스로 변환하여 job에 주입."""
        job, ti = worker.job, worker.ti
        for k, v in ti.injection.items():
            if isinstance(v, dict) and "_smblock" in v:
                pool_name = v["_smblock"]
                shape = tuple(v["shape"])
                maxsize = v["maxsize"]
                lock = v["lock"]
                if pool_name in cls._pools:
                    setattr(job, k, cls._pools[pool_name])
                else:
                    try:
                        pool = SmBlock(pool_name, shape, maxsize, create=False, lock=lock)
                        setattr(job, k, pool)
                    except Exception as e:
                        print(f"[SmBlockHandler] {ti.task_id}.{k}: attach failed: {e}")

    @classmethod
    def close_all(cls):
        """모든 SmBlock 정리."""
        for name, pool in cls._pools.items():
            try:
                pool.close()
            except Exception as e:
                print(f"[SmBlockHandler] '{name}' close failed: {e}")
        cls._pools.clear()
