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

import os
import re
import subprocess
from pathlib import Path
from typing import Optional


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


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


def run(cmd: list[str]) -> str:
    """racadm 호출을 간단히 실행 (stdout만 수집)"""
    try:
        # join하여 getoutput로 호출 (Bash와 비슷한 동작)
        return subprocess.getoutput(" ".join(cmd))
    except Exception as e:
        return f""  # 실패 시 빈 문자열


def parse_single_value(pattern: str, text: str) -> Optional[str]:
    m = re.search(pattern, text, flags=re.IGNORECASE)
    return m.group(1).strip() if m else None


def parse_mac_list_from_lines(text: str, fqdd_key: str) -> Optional[str]:
    """
    특정 FQDD 라인을 찾아 MAC 주소를 반환하는 간단한 도우미.
    (Bash의 grep/awk 파이프를 정규표현식으로 대체)
    """
    # MAC 주소 패턴
    mac_pat = r"([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})"
    # 해당 FQDD 문자열이 포함된 줄과 가까운 곳에서 MAC을 찾는 간단한 방식
    # (원 스크립트는 awk로 이전 줄을 보는 등 상세하지만 여기선 단순화)
    block_pat = rf"{re.escape(fqdd_key)}.*?{mac_pat}"
    m = re.search(block_pat, text, flags=re.IGNORECASE | re.DOTALL)
    if m:
        return m.group(1)
    # 라인 전체에서 MAC만 스캔하는 fallback
    m2 = re.search(mac_pat, text, flags=re.IGNORECASE)
    return m2.group(1) if m2 else None


def fetch_idrac_info_one(ip: str, output_dir: Path) -> None:
    # getsysinfo / hwinventory 호출
    getsysinfo = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "getsysinfo"])
    hwinventory = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "hwinventory"])

    # 서비스 태그
    svc_tag = parse_single_value(r"SVC\s*Tag\s*=\s*(\S+)", getsysinfo)
    if not svc_tag:
        print(f"Failed to retrieve SVC Tag for IP: {ip}")
        return

    # iDRAC MAC
    idrac_mac = parse_single_value(r"MAC Address\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)

    # NIC.Integrated MAC (1-1-1, 1-2-1)
    integrated_1 = parse_single_value(r"NIC\.Integrated\.1-1-1.*?([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})",
                                      getsysinfo)
    integrated_2 = parse_single_value(r"NIC\.Integrated\.1-2-1.*?([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})",
                                      getsysinfo)

    # Onboard MAC (Embedded 1-1-1, 2-1-1)
    onboard_1 = parse_single_value(r"NIC\.Embedded\.1-1-1.*?([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})",
                                   getsysinfo)
    onboard_2 = parse_single_value(r"NIC\.Embedded\.2-1-1.*?([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})",
                                   getsysinfo)

    # 벤더(메모리/SSD) 첫 글자 모음 (원 스크립트는 uniq+sort+cut -c1)
    # 여기서는 Manufacturer= 값을 수집해 첫 글자만 취합 후 중복 제거.
    mem_vendors = re.findall(r"DIMM.*?Manufacturer\s*=\s*(.+)", hwinventory, flags=re.IGNORECASE)
    mem_vendors = [v.strip() for v in mem_vendors if v.strip()]
    memory = "".join(sorted(set(v[0] for v in mem_vendors if v)))

    ssd_vendors = re.findall(r"Disk\.Bay.*?Manufacturer\s*=\s*(.+)", hwinventory, flags=re.IGNORECASE)
    ssd_vendors = [v.strip() for v in ssd_vendors if v.strip()]
    ssd = "".join(sorted(set(v[0] for v in ssd_vendors if v)))

    # 파일 저장
    out_file = output_dir / f"{svc_tag}.txt"
    with out_file.open("w", encoding="utf-8", newline="\n") as f:
        f.write(f"{svc_tag}\n")
        f.write(f"{integrated_1 or ''}\n")
        f.write(f"{integrated_2 or ''}\n")
        f.write(f"{onboard_1 or ''}\n")
        f.write(f"{onboard_2 or ''}\n")
        f.write(f"{idrac_mac or ''}\n")
        f.write(f"{memory}\n")
        f.write(f"{ssd}\n")


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

    output_dir = resolve_output_dir()

    # Bash 스크립트는 파일 전체를 cat 하여 '하나의 IP'로 사용했지만,
    # 여기서는 줄 단위로 모두 처리(한 줄만 있어도 동일하게 동작).
    ips = [line.strip() for line in ip_path.read_text(encoding="utf-8").splitlines() if line.strip()]
    if not ips:
        print("No IP addresses found in the file.")
        return

    # 순차 처리 (필요하면 ThreadPoolExecutor로 병렬화 가능)
    for ip in ips:
        try:
            fetch_idrac_info_one(ip, output_dir)
        except Exception as e:
            print(f"Error processing {ip}: {e}")

    # 원본 Bash는 마지막에 입력 파일 삭제: rm -f $IP_FILE
    try:
        ip_path.unlink(missing_ok=True)
    except Exception:
        pass

    print("정보 수집 완료.")


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

    start = time.time()
    main(sys.argv[1])
    end = time.time()
    elapsed = int(end - start)
    h, m, s = elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60
    print(f"수집 완료 시간: {h} 시간, {m} 분, {s} 초.")
