from __future__ import annotations
import os
import argparse
import sys
import logging
from pathlib import Path
from collections import OrderedDict
import pandas as pd

# -----------------------------------------------------------------------------
# Logging Configuration
# -----------------------------------------------------------------------------
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [INFO] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)

# -----------------------------------------------------------------------------
# Path Resolution
# -----------------------------------------------------------------------------
def resolve_data_root() -> Path:
    """
    Priority:
      1) Env var IDRAC_DATA_DIR (absolute/relative OK)
      2) nearest parent of this file that contains a 'data' folder
      3) ./data under current working directory
    """
    env = os.getenv("IDRAC_DATA_DIR")
    if env:
        return Path(env).expanduser().resolve()

    here = Path(__file__).resolve()
    for p in [here] + list(here.parents):
        if (p / "data").is_dir():
            return (p / "data").resolve()

    # Fallback to current working directory assumption
    cwd_data = Path.cwd() / "data"
    if cwd_data.is_dir():
        return cwd_data.resolve()
        
    # Final fallback: Assume we are in data/server_list/ -> go up two levels
    return here.parent.parent

DATA_ROOT = resolve_data_root()

# Default Paths (can be overridden by args)
DEFAULT_GUID_INPUT = DATA_ROOT / "repository" / "guid_file"
DEFAULT_GPU_INPUT = DATA_ROOT / "repository" / "gpu_serial"
DEFAULT_MAC_INPUT = DATA_ROOT / "repository" / "mac"

DEFAULT_GUID_LIST = DATA_ROOT / "server_list" / "guid_list.txt"
DEFAULT_GPU_LIST = DATA_ROOT / "server_list" / "gpu_serial_list.txt"
DEFAULT_MAC_LIST = DATA_ROOT / "server_list" / "server_list.txt"

DEFAULT_GUID_OUTPUT = DATA_ROOT / "temp" / "staging" / "XE9680_GUID.xlsx"
DEFAULT_GPU_OUTPUT = DATA_ROOT / "temp" / "staging" / "GPU_SERIALS.xlsx"
DEFAULT_MAC_OUTPUT = DATA_ROOT / "temp" / "staging" / "mac_info.xlsx"

# -----------------------------------------------------------------------------
# Utility Functions
# -----------------------------------------------------------------------------
def read_lines_any_encoding(path: Path) -> list[str]:
    """Read text file trying common encodings."""
    if not path.is_file():
        return []
        
    encodings = ["utf-8-sig", "utf-8", "cp949", "euc-kr", "latin-1"]
    for enc in encodings:
        try:
            with path.open("r", encoding=enc, errors="strict") as f:
                return f.read().splitlines()
        except Exception:
            continue
    # last resort
    with path.open("r", encoding="utf-8", errors="replace") as f:
        return f.read().splitlines()

def parse_txt_key_value(file_path: Path) -> dict:
    """
    Parse standarized Key: Value files (GPU, GUID).
    First line is assumed to be S/T (Service Tag).
    """
    lines = read_lines_any_encoding(file_path)
    if not lines:
        return {}

    data = OrderedDict()
    data["S/T"] = lines[0].strip()

    for raw in lines[1:]:
        line = raw.strip()
        if not line or ":" not in line:
            continue
        key, value = line.split(":", 1)
        data[key.strip()] = value.strip()

    return dict(data)

# -----------------------------------------------------------------------------
# Mode: MAC
# -----------------------------------------------------------------------------
def process_mac(input_dir: Path, list_file: Path, output_xlsx: Path):
    """
    Logic from excel.py
    """
    if not list_file.is_file():
        raise FileNotFoundError(f"Server list file not found: {list_file}")

    file_names = read_lines_any_encoding(list_file)
    
    data_list: list[str] = []
    index_list: list[int | str] = []
    sequence_number = 1

    for name in file_names:
        base = (name or "").strip()
        if not base:
            continue

        txt_path = input_dir / f"{base}.txt"
        
        if not txt_path.is_file():
            # Missing file handling
            data_list.append("")
            index_list.append("")
            continue

        lines = read_lines_any_encoding(txt_path)
        for i, line in enumerate(lines):
            cleaned = (line or "").strip().upper()
            if cleaned:
                data_list.append(cleaned)
                # First line is always the service tag
                if i == 0:
                    index_list.append(sequence_number)
                    sequence_number += 1
                else:
                    index_list.append("")
            else:
                data_list.append("")
                index_list.append("")

    # Create DataFrame
    df = pd.DataFrame({
        "Index": index_list,
        "Content": data_list,
    })

    # Save without header, without index
    output_xlsx.parent.mkdir(parents=True, exist_ok=True)
    df.to_excel(output_xlsx, index=False, header=False)
    logger.info(f"[MAC] Saved to {output_xlsx}")

# -----------------------------------------------------------------------------
# Mode: GPU
# -----------------------------------------------------------------------------
def process_gpu(input_dir: Path, list_file: Path, output_xlsx: Path):
    """
    Logic from GPUTOExecl.py
    """
    if not input_dir.is_dir():
         raise FileNotFoundError(f"Input directory not found: {input_dir}")

    # File collection
    files: list[Path] = []
    if list_file and list_file.is_file():
        names = [x.strip() for x in read_lines_any_encoding(list_file) if x.strip()]
        for name in names:
            p = input_dir / f"{name}.txt"
            if p.is_file():
                files.append(p)
            else:
                logger.warning(f"[GPU] File not found: {p.name}")
    else:
        # Fallback to glob
        files = sorted(input_dir.glob("*.txt"))

    if not files:
        logger.warning("[GPU] No files to process.")
        return

    # Parse
    rows = []
    for p in files:
        rows.append(parse_txt_key_value(p))

    df = pd.DataFrame(rows)
    # Insert 'No' column
    df.insert(0, "No", range(1, len(df) + 1))
    
    output_xlsx.parent.mkdir(parents=True, exist_ok=True)
    df.to_excel(output_xlsx, index=False)
    logger.info(f"[GPU] Saved to {output_xlsx}")

# -----------------------------------------------------------------------------
# Mode: GUID
# -----------------------------------------------------------------------------
def process_guid(input_dir: Path, list_file: Path, output_xlsx: Path):
    """
    Logic from GUIDtxtT0Execl.py
    """
    if not input_dir.is_dir():
         raise FileNotFoundError(f"Input directory not found: {input_dir}")

    # Determine Slot Priority
    slot_priority_str = os.getenv("GUID_SLOT_PRIORITY", "")
    if slot_priority_str:
        slot_priority = [s.strip() for s in slot_priority_str.split(",") if s.strip()]
        logger.info(f"[GUID] Custom slot priority: {slot_priority}")
    else:
        slot_priority = ['38', '39', '37', '36', '32', '33', '34', '35', '31', '40']
        logger.info(f"[GUID] Default slot priority: {slot_priority}")

    # File collection
    files: list[Path] = []
    names = []
    if list_file and list_file.is_file():
        names = [x.strip() for x in read_lines_any_encoding(list_file) if x.strip()]
    
    # Process
    rows = []
    for name in names:
        txt_path = input_dir / f"{name}.txt"
        if not txt_path.is_file():
            logger.warning(f"[GUID] File not found: {txt_path.name}")
            continue

        raw_data = parse_txt_key_value(txt_path)
        
        # Reorder and reconstruct GUID
        ordered_data = OrderedDict()
        ordered_data["S/T"] = raw_data.get("S/T", "")

        new_guid_list = []
        for slot_num in slot_priority:
            slot_key = f"Slot.{slot_num}"
            val = raw_data.get(slot_key)
            if val:
                ordered_data[slot_key] = val
                if val != "Not Found" and ":" in val:
                    clean_hex = val.replace(":", "").upper()
                    new_guid_list.append(f"0x{clean_hex}")

        # GUID Selection logic
        if new_guid_list:
            ordered_data["GUID"] = ";".join(new_guid_list)
        elif "GUID" in raw_data:
            ordered_data["GUID"] = raw_data["GUID"]
        
        # Add remaining fields
        for k, v in raw_data.items():
            if k not in ordered_data:
                ordered_data[k] = v

        rows.append(dict(ordered_data))

    if not rows:
        logger.warning("[GUID] No data processed.")
        # Create empty excel if needed or just return
        return

    df = pd.DataFrame(rows)
    df.insert(0, "No", range(1, len(df) + 1))

    output_xlsx.parent.mkdir(parents=True, exist_ok=True)
    df.to_excel(output_xlsx, index=False)
    logger.info(f"[GUID] Saved to {output_xlsx}")


# -----------------------------------------------------------------------------
# Main Entry Point
# -----------------------------------------------------------------------------
def main():
    parser = argparse.ArgumentParser(description="Unified Excel Generator")
    parser.add_argument("--mode", required=True, choices=["mac", "gpu", "guid"], help="Generation mode")
    parser.add_argument("--input-dir", type=Path, help="Input directory (optional override)")
    parser.add_argument("--list-file", type=Path, help="List file (optional override)")
    parser.add_argument("--output-xlsx", type=Path, help="Output XLSX path (optional override)")
    
    args = parser.parse_args()

    mode = args.mode
    
    # Determine effective paths based on mode and overrides
    if mode == "mac":
        input_dir = args.input_dir or DEFAULT_MAC_INPUT
        list_file = args.list_file or DEFAULT_MAC_LIST
        output_xlsx = args.output_xlsx or DEFAULT_MAC_OUTPUT
        process_mac(input_dir, list_file, output_xlsx)
        
    elif mode == "gpu":
        input_dir = args.input_dir or DEFAULT_GPU_INPUT
        list_file = args.list_file or DEFAULT_GPU_LIST
        output_xlsx = args.output_xlsx or DEFAULT_GPU_OUTPUT
        process_gpu(input_dir, list_file, output_xlsx)
        
    elif mode == "guid":
        input_dir = args.input_dir or DEFAULT_GUID_INPUT
        list_file = args.list_file or DEFAULT_GUID_LIST
        output_xlsx = args.output_xlsx or DEFAULT_GUID_OUTPUT
        process_guid(input_dir, list_file, output_xlsx)

if __name__ == "__main__":
    main()
