#!/usr/bin/env python3
"""
통합 GUID 수집 스크립트
프로파일 기반으로 InfiniBand GUID 수집
"""
import sys
import yaml
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import logging

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" / "guid_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, profiles['default'])
    
    # 환경변수에서 슬롯 우선순위 읽기 (custom_priority 프로파일용)
    if not profile.get('slot_priority'):
        env_priority = os.getenv("GUID_SLOT_PRIORITY", "")
        if env_priority:
            profile['slot_priority'] = [int(s.strip()) for s in env_priority.split(",") if s.strip()]
        else:
            profile['slot_priority'] = profiles['default']['slot_priority']
    
    return profile


def collect_guid_for_ip(ip: str, profile: dict, output_dir: Path) -> tuple:
    """단일 IP에 대한 GUID 정보 수집 (PortGUID_v1.py 로직 기반)"""
    try:
        logging.info(f"[{ip}] DEBUG: Running Unified GUID Script v2.2 (PortGUID_v1 Logic)")
        
        # IDRAC credentials from env or default
        idrac_user = os.getenv("IDRAC_USER", "root")
        idrac_pass = os.getenv("IDRAC_PASS", "calvin")

        # 1. Get Service Tag (using raw subprocess like v1)
        cmd_getsysinfo = [
            "racadm", "-r", ip, "-u", idrac_user, "-p", idrac_pass, "getsysinfo"
        ]
        # v1 uses subprocess.getoutput which runs with shell=True/standard shell behavior. 
        # Here we replicate it or use subprocess.run with capture_output.
        # PortGUID_v1 uses: getsysinfo = subprocess.getoutput(" ".join(cmd_getsysinfo))
        
        # Using subprocess.run for better control but mimicking the command structure
        import subprocess
        import re
        
        try:
            # mimic v1: Use the exact same command string style
            proc_info = subprocess.run(cmd_getsysinfo, capture_output=True, text=True, timeout=60)
            getsysinfo = proc_info.stdout
        except Exception as e:
            return ip, False, f"SVC Tag 수집 실패: {e}"

        svc_tag_match = re.search(r"SVC Tag\s*=\s*(\S+)", getsysinfo)
        svc_tag = svc_tag_match.group(1) if svc_tag_match else None

        if not svc_tag:
            return ip, False, "서비스 태그 추출 실패 (Regex miss)"

        # 2. Get InfiniBand Page List
        cmd_list = [
            "racadm", "-r", ip, "-u", idrac_user, "-p", idrac_pass, "get", "InfiniBand.VndrConfigPage"
        ]
        proc_list = subprocess.run(cmd_list, capture_output=True, text=True, timeout=60)
        output_list = proc_list.stdout

        # 3. Parse Page Map
        matches = re.findall(
            r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]",
            output_list
        )

        slot_to_guid = {}
        # 4. Fetch Detail for each page
        for number, slot in matches:
            cmd_detail = [
                "racadm", "-r", ip, "-u", idrac_user, "-p", idrac_pass,
                "get", f"InfiniBand.VndrConfigPage.{number}"
            ]
            proc_detail = subprocess.run(cmd_detail, capture_output=True, text=True, timeout=60)
            output_detail = proc_detail.stdout
            
            match_guid = re.search(r"PortGUID=(\S+)", output_detail)
            port_guid = match_guid.group(1) if match_guid else None
            
            if port_guid:
                slot_to_guid[int(slot)] = port_guid

        # 5. Determine Priority (Preference: Profile > Env > Default(v1 logic))
        # Logic from PortGUID_v1.py:
        # if total_slots == 4: order = ...
        # elif total_slots == 10: order = ...
        
        # We will use the profile's logic if available, but fallback to v1 logic if profile is 'default' and matches slot counts
        desired_order = profile.get('slot_priority', [])
        
        # If profile didn't specify (or empty), use v1 adaptive logic
        if not desired_order:
            total_slots = len(matches)
            if total_slots == 4:
                desired_order = [38, 37, 32, 34]
            elif total_slots == 10:
                desired_order = [38, 39, 37, 36, 32, 33, 34, 35, 31, 40]
            else:
                desired_order = [int(slot) for _, slot in matches] # Just collection order
            logging.info(f"[{ip}] DEBUG: Using Adaptive Priority (Count={total_slots}): {desired_order}")
        else:
            logging.info(f"[{ip}] DEBUG: Using Profile Priority: {desired_order}")

        # 6. Write File
        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")
            
            hex_guid_list = []
            
            # Write in desired order
            for slot in desired_order:
                guid = slot_to_guid.get(slot)
                if guid:
                    f.write(f"Slot.{slot}: {guid}\n")
                    
                    # Format
                    fmt_guid = guid.replace(':', '').upper()
                    if not fmt_guid.startswith("0X"):
                        fmt_guid = "0x" + fmt_guid
                    hex_guid_list.append(fmt_guid)
            
            # Remaining slots (if any weren't in priority list)? 
            # v1 doesn't write them if not in priority list. We will stick to v1 behavior.

            if profile.get('output_format', 'combined') == 'combined' and hex_guid_list:
                f.write(f"GUID: {';'.join(hex_guid_list)}\n")
        
        return ip, True, f"저장: {output_file.name} ({len(hex_guid_list)}개 GUID/v1_Logic)"

    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_guid.py <ip_file> [profile_name]")
        print("Available profiles: default, custom_priority")
        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', '')}")
    logging.info(f"슬롯 우선순위: {profile['slot_priority']}")
    
    # 출력 디렉토리
    # 출력 디렉토리 (스크립트 위치 기준)
    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_guid_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}대")


if __name__ == "__main__":
    main()
