#!/usr/bin/env python3
"""
통합 MAC 수집 스크립트
프로파일 기반으로 다양한 서버 타입 지원
"""
import sys
import yaml
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import logging

# core 모듈 임포트
sys.path.insert(0, str(Path(__file__).parent.parent))
from core.idrac_client import IDRACClient
from core.parsers import InfoParser

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [INFO] root: %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)


def load_profile(profile_name: str) -> dict:
    """프로파일 로드 및 상속 처리"""
    profile_file = Path(__file__).parent.parent / "profiles" / "mac_profiles.yaml"
    
    if not profile_file.exists():
        logging.error(f"프로파일 파일 없음: {profile_file}")
        sys.exit(1)
    
    with open(profile_file, 'r', encoding='utf-8') as f:
        profiles = yaml.safe_load(f)['profiles']
    
    profile = profiles.get(profile_name)
    if not profile:
        logging.error(f"프로파일 '{profile_name}' 없음. 사용 가능: {list(profiles.keys())}")
        sys.exit(1)
    
    # extends 처리
    if 'extends' in profile:
        base = profiles[profile['extends']].copy()
        base.update(profile)
        profile = base
    
    return profile


def collect_mac_for_ip(ip: str, profile: dict, output_dir: Path) -> tuple:
    """단일 IP에 대한 MAC 정보 수집"""
    try:
        client = IDRACClient(ip)
        parser = InfoParser()
        
        # 데이터 수집
        sysinfo = client.get_sysinfo()
        hwinventory = client.get_hwinventory()
        
        # 서비스 태그
        svc_tag = parser.extract_service_tag(sysinfo)
        if not svc_tag:
            return ip, False, "서비스 태그 추출 실패"
        
        # NIC MAC 주소
        # NIC MAC 주소
        # sysinfo보다 hwinventory가 더 상세한 정보(Slot NIC 등)를 포함하므로 변경
        nic_macs = parser.extract_nic_macs_from_hwinventory(hwinventory, profile['nic_patterns'])
        
        # iDRAC MAC
        idrac_mac = None
        if profile.get('include_idrac_mac'):
            idrac_mac = parser.extract_idrac_mac(sysinfo)
        
        # InfiniBand (선택적)
        ib_guids = {}
        if 'infiniband_slots' in profile:
            # Legacy logic: Fetch from swinventory (checking previous line of FQDD)
            swinventory = client.get_swinventory()
            ib_guids = parser.extract_infiniband_macs_from_swinventory(swinventory, profile['infiniband_slots'])
        
        # 벤더 정보
        memory_vendors = []
        disk_vendors = []
        boss_disk_vendors = []

        if profile.get('include_memory_vendor'):
            memory_vendors = parser.extract_vendors(hwinventory, "DIMM")

        if profile.get('include_disk_vendor'):
            if profile.get('exclude_boss'):
                # 일반 디스크 벤더 (BOSS 제외)
                regular_vendors = set()
                for prefix in ["Disk.Bay.", "Disk.M.2.", "Disk.Slot."]:
                    regular_vendors.update(parser.extract_vendors(hwinventory, prefix))
                disk_vendors = sorted(regular_vendors)
                # BOSS 디스크 벤더 별도 수집 (Disk.Direct. 중 BOSS 포함 항목만)
                boss_disk_vendors = parser.extract_vendors(hwinventory, "Disk.Direct.", instance_must_contain="BOSS")
            else:
                disk_vendors = parser.extract_vendors(hwinventory, "Disk.")
        
        # 파일 저장
        output_file = output_dir / f"{svc_tag}.txt"
        with output_file.open('w', encoding='utf-8', newline='\n') as f:
            f.write(f"{svc_tag}\n")
            
            # NIC MAC (순서 유지)
            for pattern in profile['nic_patterns']:
                f.write(f"{nic_macs.get(pattern) or ''}\n")
            
            # InfiniBand (있을 경우)
            if ib_guids:
                for slot in profile['infiniband_slots']:
                    f.write(f"{ib_guids.get(slot) or ''}\n")
            
            # iDRAC MAC
            if idrac_mac:
                f.write(f"{idrac_mac}\n")
            
            # 벤더 정보
            for vendor in memory_vendors:
                f.write(f"{vendor}\n")
            for vendor in disk_vendors:
                f.write(f"{vendor}\n")
            for vendor in boss_disk_vendors:
                f.write(f"{vendor}\n")
        
        return ip, True, f"저장: {output_file.name}"
    
    except Exception as e:
        logging.error(f"[{ip}] 오류: {e}")
        return ip, False, f"오류: {e}"


def main():
    if len(sys.argv) < 2:
        print("Usage: python collect_mac.py <ip_file> [profile_name]")
        print("Available profiles: default, xe9680_h200_10ea, type11, jp_54ea, sp_18ea")
        sys.exit(1)
    
    ip_file = Path(sys.argv[1])
    profile_name = sys.argv[2] if len(sys.argv) > 2 else "default"
    
    if not ip_file.exists():
        logging.error(f"IP 파일 없음: {ip_file}")
        sys.exit(1)
    
    # 프로파일 로드
    profile = load_profile(profile_name)
    logging.info(f"프로파일 사용: {profile_name} - {profile.get('description', '')}")
    
    # 출력 디렉토리 (스크립트 위치 기준)
    # script: data/scripts/unified/collect_mac.py -> data_root: data/
    script_dir = Path(__file__).resolve().parent
    data_root = script_dir.parent.parent
    output_dir = data_root / "temp" / "staging"
    output_dir.mkdir(parents=True, exist_ok=True)
    
    # IP 목록 읽기
    ips = [line.strip() for line in ip_file.read_text(encoding='utf-8').splitlines() if line.strip()]
    
    if not ips:
        logging.error("IP 목록이 비어 있습니다.")
        sys.exit(1)
    
    logging.info(f"총 {len(ips)}대 처리 시작")
    
    # 병렬 처리
    ok, fail = 0, 0
    with ThreadPoolExecutor(max_workers=20) as executor:
        futures = {executor.submit(collect_mac_for_ip, ip, profile, output_dir): ip for ip in ips}
        
        for future in futures:
            ip, success, msg = future.result()
            if success:
                logging.info(f"[OK] {ip} - {msg}")
                ok += 1
            else:
                logging.error(f"[FAIL] {ip} - {msg}")
                fail += 1
    
    logging.info(f"완료: 성공 {ok}대 / 실패 {fail}대")
    
    # IP 파일 삭제 (기존 스크립트와 동일)
    try:
        ip_file.unlink(missing_ok=True)
    except Exception:
        pass


if __name__ == "__main__":
    main()
