#!/usr/bin/env python3
from __future__ import annotations

import os
import re
import time
import subprocess
from pathlib import Path


# ─────────────────────────────────────────────────────────────
# 저장 위치: 이 파일이 data/scripts/ 아래 있으면 → data/idrac_info
# 그 외여도 이 파일의 상위 폴더에 data/idrac_info 생성
def resolve_output_dir() -> Path:
    here = Path(__file__).resolve().parent
    # 통상 data/scripts/에 둘 경우 data/idrac_info 로 저장
    if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
        base = here.parent  # data
    elif (here / "data").is_dir():
        base = here / "data"
    else:
        base = here  # 그래도 현재 기준으로 생성
    out = base / "idrac_info"
    out.mkdir(parents=True, exist_ok=True)
    return out


# ─────────────────────────────────────────────────────────────
# 사용자 이름 및 비밀번호 (Bash 스크립트와 동일)
IDRAC_USER = "root"
IDRAC_PASS = "calvin"


def sh(cmd: list[str] | str) -> str:
    """racadm 호출을 간단히 실행 (stdout만 수집)"""
    if isinstance(cmd, list):
        cmd = " ".join(cmd)
    try:
        return subprocess.getoutput(cmd)
    except Exception:
        return ""


def grep_value_line(text: str, key: str, after_eq_strip=True) -> str | None:
    """
    Bash에서 `grep -i "<key>" | awk -F '=' '{print $2}' | tr -d '[:space:]'`
    에 상응하는 간단 추출기.
    """
    # 대소문자 구분 없이 key를 포함하는 라인을 찾음
    pat = re.compile(re.escape(key), re.IGNORECASE)
    for line in text.splitlines():
        if pat.search(line):
            if after_eq_strip and "=" in line:
                return line.split("=", 1)[1].strip().replace(" ", "")
            return line.strip()
    return None


def parse_after_eq(text: str, key_regex: str, strip_spaces=True) -> str | None:
    """
    정규표현식으로 키를 찾아 '=' 뒤 값을 추출.
    ex) key_regex=r"System BIOS Version"
    """
    for line in text.splitlines():
        # [Key=...] 형식의 줄은 건너뛰기
        if line.strip().startswith('[') and line.strip().endswith(']'):
            continue
            
        if re.search(key_regex, line, flags=re.IGNORECASE):
            if "=" in line:
                val = line.split("=", 1)[1]
                return val.strip().replace(" ", "") if strip_spaces else val.strip()
    return None


def fetch_idrac_info(ip: str, output_dir: Path) -> None:
    # 원 스크립트 호출 순서 및 동일 항목 수집
    hwinventory = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "hwinventory"])
    getsysinfo = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "getsysinfo"])
    sys_profile = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.SysProfileSettings"])
    proc_settings = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.ProcSettings"])
    mem_settings = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.MemSettings"])
    storage_ctrl = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "STORAGE.Controller.1"])

    svc_tag = parse_after_eq(getsysinfo, r"SVC\s*Tag")
    bios_fw = parse_after_eq(getsysinfo, r"System\s*BIOS\s*Version")
    idrac_fw = parse_after_eq(getsysinfo, r"Firmware\s*Version")
    intel_nic_fw = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "NIC.FrmwImgMenu.1"]),
                                  r"#FamilyVersion")
    onboard_nic_fw = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "NIC.FrmwImgMenu.5"]),
                                    r"#FamilyVersion")
    raid_fw = parse_after_eq(hwinventory, r"ControllerFirmwareVersion")
    bios_boot_mode = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.BiosBootSettings"]),
                                    r"BootMode")

    # SysProfileSettings 세부
    sysprof_1 = parse_after_eq(sys_profile, r"SysProfile")
    sysprof_2 = parse_after_eq(sys_profile, r"EnergyPerformanceBias")
    sysprof_3 = parse_after_eq(sys_profile, r"MemFrequency")
    sysprof_4 = parse_after_eq(sys_profile, r"ProcTurboMode")
    sysprof_5 = parse_after_eq(sys_profile, r"ProcC1E")
    sysprof_6 = parse_after_eq(sys_profile, r"ProcCStates")
    sysprof_7 = parse_after_eq(sys_profile, r"MonitorMwait")

    # ProcessorSettings
    proc_1 = parse_after_eq(proc_settings, r"LogicalProc")
    proc_2 = parse_after_eq(proc_settings, r"ProcVirtualization")
    proc_3 = parse_after_eq(proc_settings, r"LlcPrefetch")
    proc_4 = parse_after_eq(proc_settings, r"ProcX2Apic")

    # MemorySettings
    mem_1 = parse_after_eq(mem_settings, r"NodeInterleave")
    mem_2 = parse_after_eq(mem_settings, r"PPROnUCE")
    mem_3 = parse_after_eq(mem_settings, r"CECriticalSEL")

    # System.ThermalSettings
    system_thermal = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "System.ThermalSettings"])
    system_1 = parse_after_eq(system_thermal, r"ThermalProfile", strip_spaces=False)

    # Bios.IntegratedDevices
    integ_devs = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "Bios.IntegratedDevices"])
    integ_1 = parse_after_eq(integ_devs, r"SriovGlobalEnable")

    # bios.MiscSettings
    misc = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.MiscSettings"])
    misc_1 = parse_after_eq(misc, r"ErrPrompt")

    # iDRAC.* 다양한 설정
    tz = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.Time.Timezone"]),
                        r"Timezone")
    active_nic = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.CurrentNIC"]),
                                r"ActiveNIC")
    ipv4_dhcp = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.CurrentIPv4"]),
                               r"DHCPEnable")
    ipv6_enable = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.CurrentIPv6"]),
                                 r"Enable")
    redfish_enable = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.Redfish.Enable"]),
                                    r"Enable")
    ssh_enable = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SSH"]),
                                r"Enable")

    ad_user_domain = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.USERDomain.1.Name"]),
                                    r"Name")
    ad_dc1 = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ActiveDirectory.DomainController1"]),
                            r"DomainController1")
    ad_grp1_name = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.1.Name"]),
                                  r"Name")
    ad_grp1_domain = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.1.Domain"]),
                                    r"Domain")
    ad_grp1_priv = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.1.Privilege"]),
                                  r"Privilege")
    ad_grp2_name = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.2.Name"]),
                                  r"Name")
    ad_grp2_domain = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.2.Domain"]),
                                    r"Domain")
    ad_grp2_priv = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.2.Privilege"]),
                                  r"Privilege")

    syslog_enable = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SysLog.SysLogEnable"]),
                                   r"SysLogEnable")
    syslog_s1 = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SysLog.Server1"]),
                               r"Server1")
    syslog_s2 = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SysLog.Server2"]),
                               r"Server2")
    syslog_port = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SysLog.Port"]),
                                 r"Port")
    kvm_port = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.VirtualConsole.Port"]),
                              r"Port")

    if not svc_tag:
        print(f"Failed to retrieve SVC Tag for IP: {ip}")
        return

    out_file = output_dir / f"{svc_tag}.txt"
    with out_file.open("w", encoding="utf-8", newline="\n") as f:
        f.write(f"Dell EMC Server Bios,iDRAC,R/C Setting (SVC Tag: {svc_tag})\n\n")
        f.write("------------------------------------------Firware Version 정보------------------------------------------\n")
        f.write(f"1. SVC Tag : {svc_tag}\n")
        f.write(f"2. Bios Firmware : {bios_fw or ''}\n")
        f.write(f"3. iDRAC Firmware Version : {idrac_fw or ''}\n")
        f.write(f"4. NIC Integrated Firmware Version : {intel_nic_fw or ''}\n")
        f.write(f"5. OnBoard NIC Firmware Version : {onboard_nic_fw or ''}\n")
        f.write(f"6. Raid Controller Firmware Version : {raid_fw or ''}\n\n")

        f.write("---------------------------------------------Bios 설정 정보----------------------------------------------\n")
        f.write(f"01. Bios Boot Mode : {bios_boot_mode or ''}\n")
        f.write(f"02. System Profile Settings - System Profile : {sysprof_1 or ''}\n")
        f.write(f"03. System Profile Settings - CPU Power Management : {sysprof_2 or ''}\n")
        f.write(f"04. System Profile Settings - Memory Frequency : {sysprof_3 or ''}\n")
        f.write(f"05. System Profile Settings - Turbo Boost : {sysprof_4 or ''}\n")
        f.write(f"06. System Profile Settings - C1E : {sysprof_5 or ''}\n")
        f.write(f"07. System Profile Settings - C-States : {sysprof_6 or ''}\n")
        f.write(f"08. System Profile Settings - Monitor/Mwait : {sysprof_7 or ''}\n")
        f.write(f"09. Processor Settings - Logical Processor : {proc_1 or ''}\n")
        f.write(f"10. Processor Settings - Virtualization Technology : {proc_2 or ''}\n")
        f.write(f"11. Processor Settings - LLC Prefetch : {proc_3 or ''}\n")
        f.write(f"12. Processor Settings - x2APIC Mode : {proc_4 or ''}\n")
        f.write(f"13. Memory Settings - Node Interleaving : {mem_1 or ''}\n")
        f.write(f"14. Memory Settings - DIMM Self Healing (Post Package Repair) on Uncorrectable Memory Error : {mem_2 or ''}\n")
        f.write(f"15. Memory Settings - Correctable Error Logging : {mem_3 or ''}\n")
        f.write(f"16. System Settings - Thermal Profile Optimization : {system_1 or ''}\n")
        f.write(f"17. Integrated Devices Settings - SR-IOV Global Enable : {integ_1 or ''}\n")
        f.write(f"18. Miscellaneous Settings - F1/F2 Prompt on Error : {misc_1 or ''}\n\n")

        f.write("---------------------------------------------iDRAC 설정 정보----------------------------------------------\n")
        f.write(f"01. iDRAC Settings - Timezone : {tz or ''}\n")
        f.write(f"02. iDRAC Settings - IPMI LAN Selection : {active_nic or ''}\n")
        f.write(f"03. iDRAC Settings - IPMI IP(IPv4) : {ipv4_dhcp or ''}\n")
        f.write(f"04. iDRAC Settings - IPMI IP(IPv6) : {ipv6_enable or ''}\n")
        f.write(f"05. iDRAC Settings - Redfish Support : {redfish_enable or ''}\n")
        f.write(f"06. iDRAC Settings - SSH Support : {ssh_enable or ''}\n")
        f.write(f"07. iDRAC Settings - AD User Domain Name : {ad_user_domain or ''}\n")
        f.write(f"08. iDRAC Settings - SC Server Address : {ad_dc1 or ''}\n")
        f.write(f"09. iDRAC Settings - SE AD RoleGroup Name : {ad_grp1_name or ''}\n")
        f.write(f"10. iDRAC Settings - SE AD RoleGroup Dome인 : {ad_grp1_domain or ''}\n")
        f.write(f"11. iDRAC Settings - SE AD RoleGroup Privilege : {ad_grp1_priv or ''}\n")
        f.write(f"12. iDRAC Settings - IDC AD RoleGroup Name : {ad_grp2_name or ''}\n")
        f.write(f"13. iDRAC Settings - IDC AD RoleGroup Domain : {ad_grp2_domain or ''}\n")
        f.write(f"14. iDRAC Settings - IDC AD RoleGroup Privilege : {ad_grp2_priv or ''}\n")
        f.write(f"15. iDRAC Settings - Remote Log (syslog) : {syslog_enable or ''}\n")
        f.write(f"16. iDRAC Settings - syslog server address 1 : {syslog_s1 or ''}\n")
        f.write(f"17. iDRAC Settings - syslog server address 2 : {syslog_s2 or ''}\n")
        f.write(f"18. iDRAC Settings - syslog server port : {syslog_port or ''}\n")
        f.write(f"19. iDRAC Settings - Remote KVM Nonsecure port : {kvm_port or ''}\n")


def main(ip_file: str) -> None:
    ip_path = Path(ip_file)
    if not ip_path.is_file():
        print(f"Usage: python script.py <ip_file>\nIP file {ip_file} does not exist.")
        return

    output_dir = resolve_output_dir()

    # 원 Bash는 cat $IP_FILE → 파일 전체를 하나의 IP처럼 사용(=실질적으로 "한 줄 IP")
    # 여기서는 첫 번째 비어있지 않은 줄만 사용해 동일하게 동작시킵니다.
    lines = [ln.strip() for ln in ip_path.read_text(encoding="utf-8", errors="ignore").splitlines()]
    ip = next((ln for ln in lines if ln), None)
    if not ip:
        print("No IP address found in the file.")
        return

    start = time.time()
    fetch_idrac_info(ip, output_dir)
    end = time.time()

    # 입력 파일 삭제 (원 Bash의 rm -f $IP_FILE와 동일)
    try:
        ip_path.unlink(missing_ok=True)
    except Exception:
        pass

    elapsed = int(end - start)
    h, m, s = elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60
    print("정보 수집 완료.")
    print(f"수집 완료 시간: {h} 시간, {m} 분, {s} 초.")


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