"""
racadm 출력 파싱 유틸리티
"""
import re
from typing import List, Optional, Dict, Tuple


class InfoParser:
    """racadm 명령 출력 파싱 유틸리티"""
    
    # Word boundary to ensure we don't match partial GUIDs (which are longer)
    MAC_PATTERN = re.compile(r"\b([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}\b")
    
    @staticmethod
    def extract_service_tag(sysinfo: str) -> Optional[str]:
        """
        서비스 태그 추출
        
        Args:
            sysinfo: getsysinfo 명령 출력
        
        Returns:
            서비스 태그 또는 None
        """
        m = re.search(r"SVC\s*Tag\s*=\s*(\S+)", sysinfo, re.IGNORECASE)
        return m.group(1).strip() if m else None
    
    @staticmethod
    def extract_nic_macs(sysinfo: str, nic_patterns: List[str]) -> Dict[str, Optional[str]]:
        """
        NIC MAC 주소 추출
        
        Args:
            sysinfo: getsysinfo 명령 출력
            nic_patterns: NIC 패턴 리스트 (예: ["NIC.Integrated.1-1-1", "NIC.Embedded.1-1-1"])
        
        Returns:
            {pattern: mac_address} 딕셔너리
        """
        results = {}
        for pattern in nic_patterns:
            for line in sysinfo.splitlines():
                if pattern in line:
                    mac = InfoParser.MAC_PATTERN.search(line)
                    results[pattern] = mac.group(0) if mac else None
                    break
            else:
                results[pattern] = None
        return results

    @staticmethod
    def extract_nic_macs_from_hwinventory(hwinventory: str, nic_patterns: List[str]) -> Dict[str, Optional[str]]:
        """
        hwinventory에서 NIC MAC 주소 추출
        
        Args:
            hwinventory: hwinventory 명령 출력
            nic_patterns: NIC 패턴 리스트 (예: ["NIC.Integrated.1-1-1", "NIC.Slot.1-1-1"])
        
        Returns:
            {pattern: mac_address} 딕셔너리
        """
        results = {pattern: None for pattern in nic_patterns}
        
        # hwinventory를 블록 단위로 분리 (빈 줄로 구분)
        blocks = re.split(r"\n\s*\n", hwinventory)
        
        for block in blocks:
            # 블록 내에서 FQDD 또는 InstanceID 확인
            fqdd = None
            
            # FQDD = NIC.Integrated.1-1-1 또는 [InstanceID: NIC.Integrated.1-1-1] 형식 찾기
            m_fqdd = re.search(r"FQDD\s*=\s*(\S+)", block, re.IGNORECASE)
            if m_fqdd:
                fqdd = m_fqdd.group(1).strip()
            # FQDD 키워드가 없을 경우를 대비해 InstanceID 헤더도 확인
            elif "[InstanceID:" in block:
                m_inst = re.search(r"InstanceID:\s*([^\]]+)", block)
                if m_inst:
                    fqdd = m_inst.group(1).strip()
            
            if not fqdd:
                continue
                
            # 요청된 패턴 중 하나라도 포함하는지 확인 (정확한 매칭을 위해 수정 가능하나, 보통 포함 관계면 충분)
            # 여기서는 패턴이 FQDD와 정확히 일치하거나 FQDD가 패턴을 포함하는 경우를 찾음
            matched_pattern = None
            for pattern in nic_patterns:
                # 대소문자 무시 비교
                if pattern.lower() == fqdd.lower():
                    matched_pattern = pattern
                    break
            
            # 정확히 일치하는 패턴을 못 찾았다면 다음 블록으로
            if not matched_pattern:
                continue

            # MAC 주소 찾기
            # Ethernet Address = AA:BB:CC:DD:EE:FF
            # CurrentMACAddress = AA:BB:CC:DD:EE:FF
            # MAC Address = AA:BB:CC:DD:EE:FF
            mac_match = InfoParser.MAC_PATTERN.search(block)
            
            if mac_match:
                results[matched_pattern] = mac_match.group(0)
        
        return results
    
    @staticmethod
    def extract_idrac_mac(sysinfo: str) -> Optional[str]:
        """
        iDRAC MAC 주소 추출
        
        Args:
            sysinfo: getsysinfo 명령 출력
        
        Returns:
            iDRAC MAC 주소 또는 None
        """
        for line in sysinfo.splitlines():
            if "MAC Address" in line:
                mac = InfoParser.MAC_PATTERN.search(line)
                if mac:
                    return mac.group(0)
        return None
    
    @staticmethod
    def extract_vendors(hwinventory: str, component_prefix: str, instance_must_contain: str = None) -> List[str]:
        """
        컴포넌트 벤더 추출

        Args:
            hwinventory: hwinventory 명령 출력
            component_prefix: 컴포넌트 접두사 ("DIMM", "Disk.Bay", "BOSS" 등)
            instance_must_contain: InstanceID/FQDD에 반드시 포함돼야 할 문자열 (예: "BOSS")

        Returns:
            벤더 리스트 (중복 제거, 정렬됨)
        """
        vendors = set()
        blocks = re.split(r"\n\s*\n", hwinventory)

        for block in blocks:
            # Check if this block is for the target component using InstanceID or FQDD
            is_target_component = False
            matched_id = None

            # Check InstanceID header: [InstanceID: DIMM.Socket.A1]
            if f"[InstanceID: {component_prefix}" in block:
                is_target_component = True
                m_inst = re.search(r"\[InstanceID:\s*([^\]]+)\]", block)
                if m_inst:
                    matched_id = m_inst.group(1).strip()

            # Also check FQDD line if InstanceID header check fails (fallback)
            elif not is_target_component:
                m_fqdd = re.search(r"^FQDD\s*=\s*(\S+)", block, re.MULTILINE)
                if m_fqdd and component_prefix in m_fqdd.group(1):
                    is_target_component = True
                    matched_id = m_fqdd.group(1).strip()

            if not is_target_component:
                continue

            # instance_must_contain 필터 적용
            if instance_must_contain and matched_id and instance_must_contain not in matched_id:
                continue

            m = re.search(r"Manufacturer\s*=\s*(.+)", block, re.IGNORECASE)
            if m:
                vendors.add(m.group(1).strip())

        return sorted(vendors)
    
    @staticmethod
    def extract_infiniband_page_map(pages_output: str) -> List[Tuple[str, str]]:
        """
        InfiniBand 페이지 번호와 슬롯 번호 매핑 추출
        
        Args:
            pages_output: get InfiniBand.VndrConfigPage 출력
        
        Returns:
            [(page_number, slot_number), ...] 리스트
        """
        # InfiniBand.VndrConfigPage.1 [Key=InfiniBand.Slot.38-1#VndrConfigPage]
        matches = re.findall(
            r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]",
            pages_output
        )
        return matches
    
    @staticmethod
    def extract_port_guid(page_detail: str) -> Optional[str]:
        """
        InfiniBand 페이지 상세에서 PortGUID 추출
        
        Args:
            page_detail: get InfiniBand.VndrConfigPage.{N} 출력
        
        Returns:
            PortGUID 값 또는 None
        """
        m = re.search(r"PortGUID=(\S+)", page_detail)
        return m.group(1) if m else None
    
    @staticmethod
    def extract_value_by_pattern(text: str, pattern: str) -> Optional[str]:
        """
        패턴으로 값 추출 (= 뒤의 값)
        
        Args:
            text: 검색할 텍스트
            pattern: 검색 패턴
        
        Returns:
            추출된 값 또는 None
        """
        for line in text.splitlines():
            if pattern.lower() in line.lower():
                if '=' in line:
                    return line.split('=', 1)[1].strip()
        return None

    @staticmethod
    def extract_infiniband_macs_from_swinventory(swinventory: str, target_slots: List[int]) -> Dict[int, str]:
        """
        swinventory에서 InfiniBand 슬롯의 MAC 주소 추출
        (FQDD 라인 바로 윗줄에 MAC 주소가 있다는 가정 - 레거시 스크립트 로직)
        
        Args:
            swinventory: swinventory 명령 출력
            target_slots: 추출할 슬롯 번호 리스트
            
        Returns:
            {slot: mac_address} 딕셔너리
        """
        results = {}
        prev_line = ""
        
        # 슬롯별 패턴 미리 생성 (예: "FQDD = InfiniBand.Slot.31-1")
        slot_patterns = {slot: f"FQDD = InfiniBand.Slot.{slot}-1" for slot in target_slots}
        
        for line in swinventory.splitlines():
            for slot, pattern in slot_patterns.items():
                if pattern in line:
                    # 이전 라인에서 MAC 찾기
                    mac = InfoParser.MAC_PATTERN.search(prev_line)
                    if mac:
                        results[slot] = mac.group(0)
            prev_line = line
            
        return results
    
    @staticmethod
    def extract_gpu_serials(hwinventory: str) -> Dict[str, str]:
        """
        iDRAC hwinventory 전체 텍스트에서 GPU(Video.Slot.*) 블록을 찾아
        {FQDD(or InstanceID): SerialNumber} 딕셔너리로 반환.
        
        Args:
            hwinventory: hwinventory 명령 출력
            
        Returns:
            {fqdd: serial} 딕셔너리
        """
        results = {}

        # 빈 줄 기준 블록 분할(여러 개의 개행을 하나의 경계로)
        blocks = re.split(r"\n\s*\n", hwinventory, flags=re.MULTILINE)

        for block in blocks:
            # GPU(Video) 블록만 처리
            # Video.Slot.X 또는 유사 패턴
            if not re.search(r"\[?InstanceID:\s*Video\.Slot\.", block):
                continue

            # FQDD 우선, 없으면 [InstanceID: ...]에서 추출
            fqdd = None
            m_fqdd = re.search(r"^FQDD\s*=\s*([^\r\n]+)", block, flags=re.MULTILINE)
            if m_fqdd:
                fqdd = m_fqdd.group(1).strip()
            else:
                m_hdr = re.search(r"\[InstanceID:\s*(Video\.Slot\.[^\]\r\n]+)\]", block)
                if m_hdr:
                    fqdd = m_hdr.group(1).strip()

            if not fqdd:
                continue

            # SerialNumber 추출
            m_sn = re.search(r"^SerialNumber\s*=\s*([^\r\n]+)", block, flags=re.MULTILINE)
            serial = m_sn.group(1).strip() if m_sn else "Not Found"

            results[fqdd] = serial

        return results
