import os
import re
import subprocess
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
import logging

from dotenv import load_dotenv, find_dotenv

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

# ─────────────────────────────────────────────────────────────
# .env 자동 탐색 로드 (현재 파일 기준 상위 디렉터리까지 검색)
load_dotenv(find_dotenv())

IDRAC_USER = os.getenv("IDRAC_USER")
IDRAC_PASS = os.getenv("IDRAC_PASS")


def resolve_output_dir() -> Path:
    """
    실행 위치와 무관하게 결과를 data/idrac_info 밑으로 저장.
    - 스크립트가 data/scripts/ 에 있다면 → data/idrac_info
    - 그 외 위치라도 → (스크립트 상위 폴더)/idrac_info
    """
    here = Path(__file__).resolve().parent  # .../data/scripts 또는 다른 폴더
    # case 1: .../data/scripts  → data/idrac_info
    if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
        base = here.parent  # data
    # case 2: .../scripts (상위가 data가 아닐 때도 상위 폴더를 base로 사용)
    elif here.name.lower() == "scripts":
        base = here.parent
    # case 3: 일반적인 경우: 현재 파일의 상위 폴더
    else:
        base = here.parent

    out = base / "temp" / "staging"
    out.mkdir(parents=True, exist_ok=True)
    return out


def fetch_idrac_info(idrac_ip: str, output_dir: Path) -> None:
    try:
        # 서비스 태그 가져오기 (get 제외)
        cmd_getsysinfo = [
            "racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "getsysinfo"
        ]
        getsysinfo = subprocess.getoutput(" ".join(cmd_getsysinfo))
        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:
            logging.error(f"Failed to retrieve SVC Tag for IP: {idrac_ip}")
            return

        # InfiniBand.VndrConfigPage 목록 가져오기
        cmd_list = [
            "racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "get", "InfiniBand.VndrConfigPage"
        ]
        output_list = subprocess.getoutput(" ".join(cmd_list))

        # InfiniBand.VndrConfigPage.<숫자> 및 Key 값 추출
        matches = re.findall(
            r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]",
            output_list
        )

        # 결과 저장 파일
        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")

            # --- 슬롯/GUID 수집 후 원하는 순서로 기록 ---
            slot_to_guid: dict[str, str] = {}
            slots_in_match_order: list[str] = []

            # 각 페이지 상세 조회
            for number, slot in matches:
                cmd_detail = [
                    "racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "",
                    "get", f"InfiniBand.VndrConfigPage.{number}"
                ]
                output_detail = subprocess.getoutput(" ".join(cmd_detail))

                # PortGUID 추출
                match_guid = re.search(r"PortGUID=(\S+)", output_detail)
                port_guid = match_guid.group(1) if match_guid else "Not Found"

                s = str(slot)
                slot_to_guid[s] = port_guid
                slots_in_match_order.append(s)

            # 검색된 슬롯 개수에 따라 출력 순서 결정
            # 환경변수에서 슬롯 우선순위 읽기 (GUIDtxtT0Execl.py와 동일)
            slot_priority_str = os.getenv("GUID_SLOT_PRIORITY", "")
            
            if slot_priority_str:
                # 사용자 지정 슬롯 우선순위 사용
                desired_order = [s.strip() for s in slot_priority_str.split(",") if s.strip()]
                logging.info(f"사용자 지정 슬롯 우선순위 사용: {desired_order}")
            else:
                # 기본 우선순위 (슬롯 개수에 따라)
                total_slots = len(slots_in_match_order)
                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 = slots_in_match_order
                logging.info(f"기본 슬롯 우선순위 사용 (슬롯 {total_slots}개): {desired_order}")

            # 지정된 순서대로 파일에 기록 + GUID 요약 생성
            hex_guid_list: list[str] = []
            for s in desired_order:
                guid = slot_to_guid.get(s, "Not Found")
                f.write(f"Slot.{s}: {guid}\n")
                if guid != "Not Found":
                    hex_guid_list.append(f"0x{guid.replace(':', '').upper()}")

            if hex_guid_list:
                # GUID 순서: 사용자 지정 순서대로 세미콜론으로 연결
                f.write(f"GUID: {';'.join(hex_guid_list)}\n")
                logging.info(f"GUID 합치기 완료: {len(hex_guid_list)}개 슬롯")


    except Exception as e:
        logging.error(f"Error processing IP {idrac_ip}: {e}")


def main(ip_file: str) -> None:
    ip_path = Path(ip_file)
    if not ip_path.is_file():
        logging.error(f"IP file {ip_file} does not exist.")
        return

    output_dir = resolve_output_dir()  # ← 여기서 OS 무관 저장 위치 확정 (data/idrac_info)
    # print(f"[debug] output_dir = {output_dir}")  # 필요 시 확인

    with ip_path.open("r", encoding="utf-8") as file:
        ip_addresses = [line.strip() for line in file if line.strip()]

    # 스레드풀
    with ThreadPoolExecutor(max_workers=100) as executor:
        future_to_ip = {executor.submit(fetch_idrac_info, ip, output_dir): ip for ip in ip_addresses}

        for future in as_completed(future_to_ip):
            ip = future_to_ip[future]
            try:
                future.result()
                logging.info(f"Completed: {ip}")
            except Exception as e:
                logging.error(f"Error processing {ip}: {e}")


if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2:
        logging.error("Usage: python script.py <ip_file>")
        sys.exit(1)

    main(sys.argv[1])
