#!/usr/bin/env python3
"""
GCB 政府組態基準檢測工具 - Ubuntu 22.04 LTS (TWGCB-01-014)

用法:
  sudo python3 gcb_check_ubuntu.py
  sudo python3 gcb_check_ubuntu.py --exclusion exclusion.json

參數:
  --exclusion FILE    排除清單 JSON（從管理平台匯出）

預設讀取同目錄下的 ubuntu_gcb.json
產出 JSON 報表（含修復建議），可上傳至管理平台
"""
import json, subprocess, socket, datetime, sys, os, argparse

def load_checks(json_path):
    with open(json_path, 'r', encoding='utf-8') as f:
        items = json.load(f)
    return [it for it in items if it.get('check_cmd')]

def load_exclusions(exc_path):
    if not exc_path or not os.path.exists(exc_path):
        return set()
    with open(exc_path, 'r', encoding='utf-8') as f:
        data = json.load(f)
    return set(item['id'] for item in data.get('excluded_items', []))

def run(cmd):
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=15)
        output = (r.stdout + r.stderr).strip()
        for line in reversed(output.split('\n')):
            if 'PASS' in line: return 'pass', output
            if 'FAIL' in line: return 'fail', output
        return 'fail', output
    except Exception as e:
        return 'error', str(e)

def main():
    parser = argparse.ArgumentParser(description='GCB Ubuntu 22.04 檢測工具')
    parser.add_argument('gcb_json', nargs='?', default=None, help='GCB 定義檔路徑')
    parser.add_argument('--exclusion', '-e', default=None, help='排除清單 JSON')
    args = parser.parse_args()

    if os.geteuid() != 0:
        print("[!] 請使用 sudo 執行: sudo python3 gcb_check_ubuntu.py"); sys.exit(1)

    json_path = args.gcb_json or os.path.join(os.path.dirname(os.path.abspath(__file__)), 'ubuntu_gcb.json')
    if not os.path.exists(json_path):
        print(f"[!] 找不到 GCB 定義檔: {json_path}"); sys.exit(1)

    checks = load_checks(json_path)
    exclusions = load_exclusions(args.exclusion)

    active_checks = [c for c in checks if c['id'] not in exclusions]
    excluded_checks = [c for c in checks if c['id'] in exclusions]

    print(f"\n{'='*65}")
    print(f"  GCB 檢測工具 - Ubuntu 22.04 LTS (TWGCB-01-014)")
    print(f"  主機: {socket.gethostname()}")
    print(f"  時間: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"  檢測項目: {len(active_checks)} 項  |  排除: {len(excluded_checks)} 項")
    print(f"{'='*65}\n")

    results = []
    total = len(active_checks)
    for i, c in enumerate(active_checks, 1):
        status, output = run(c['check_cmd'])
        icon = '\u2705' if status == 'pass' else ('\u274c' if status == 'fail' else '\u26a0\ufe0f')
        print(f"  [{i:3d}/{total}] {icon} {c['id']} {c['name']} -> {status.upper()}")
        result = {"id": c['id'], "category": c.get('category',''), "name": c.get('name',''),
                  "expected": c.get('expected',''), "status": status, "output": output[:200]}
        if status == 'fail':
            result["remediation"] = c.get('remediation', c.get('method', '請參閱 GCB 說明文件'))
        results.append(result)

    # 排除項目也記錄
    for c in excluded_checks:
        results.append({"id": c['id'], "category": c.get('category',''), "name": c.get('name',''),
                        "expected": c.get('expected',''), "status": "excluded", "output": "已排除"})

    p = sum(1 for r in results if r['status'] == 'pass')
    f = sum(1 for r in results if r['status'] == 'fail')
    e = sum(1 for r in results if r['status'] == 'error')
    ex = sum(1 for r in results if r['status'] == 'excluded')
    rate = round(p / total * 100, 1) if total else 0

    report = {"type": "ubuntu", "gcb_id": "TWGCB-01-014", "title": "Ubuntu 22.04 LTS 政府組態基準檢測報告",
        "hostname": socket.gethostname(), "scan_time": datetime.datetime.now().isoformat(),
        "summary": {"total": total, "pass": p, "fail": f, "error": e, "excluded": ex, "compliance_rate": rate},
        "results": results}

    fname = f"gcb_ubuntu_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    with open(fname, 'w', encoding='utf-8') as fp:
        json.dump(report, fp, ensure_ascii=False, indent=2)

    print(f"\n{'='*65}")
    print(f"  \u2705 通過: {p}/{total} ({rate}%)")
    print(f"  \u274c 不通過: {f}  |  \u26a0\ufe0f 錯誤: {e}  |  \u23ed\ufe0f 排除: {ex}")
    if f > 0:
        print(f"\n  --- 不通過項目修復建議 ---")
        for r in results:
            if r['status'] == 'fail' and r.get('remediation'):
                print(f"\n  [{r['id']}] {r['name']}")
                for line in r['remediation'].split('\n'):
                    print(f"    > {line}")
    print(f"\n  \U0001f4c4 報表: {fname}")
    print(f"{'='*65}\n")

if __name__ == '__main__':
    main()
