import os
import sys
import time
import json
import shutil
from pathlib import Path
import ctypes

try:
    if hasattr(sys.stdout, "reconfigure"):
        sys.stdout.reconfigure(encoding="utf-8")
        sys.stderr.reconfigure(encoding="utf-8")
except Exception:
    pass

sys.path.insert(0, str(Path(".").resolve()))

from src.assistant_tool.client_launcher import Poe2ClientLauncher
from src.common.win32_window import find_poe2_window, ensure_window_focus, get_client_rect, is_poe2_window_focused
import scripts.reconnect_and_enter_hideout as reconnect_module
from tools.autonomous_mapping_coordinator import AutonomousMappingCoordinator


def clean_shaders():
    pass

def main(
    map_duration_sec: int = 100,
    enable_stashing: bool = False,
    max_maps: int = 1,
    force_new_map: bool = False,
    no_kmbox: bool = True,
):
    print("=" * 66, flush=True)
    print("   AUTONOMOUS POE2 - LIVE OPS MAPPING EXECUTION ENGINE (DIV 5)    ", flush=True)
    print("   [MODE: 100% PURE SOFTWARE AUTOMATION - NO KMBOX HARDWARE]       ", flush=True)
    print("=" * 66, flush=True)

    if os.name == "nt":
        try:
            from src.common.win32_window import attach_to_default_desktop
            attach_to_default_desktop()
        except Exception:
            pass

    client_log_path = r"C:\Program Files (x86)\Grinding Gear Games\Path of Exile 2\logs\Client.txt"
    start_log_offset = os.path.getsize(client_log_path) if os.path.exists(client_log_path) else 0
    cycle_start_time = time.time()

    print(f"\n[Cycle] Mốc Client.txt ban đầu: {start_log_offset} bytes | Bắt đầu lúc {time.strftime('%H:%M:%S')}", flush=True)
    print("\n[Cycle] Buoc 1: Khoi chay Client POE2 va Reconnect vao Hideout...", flush=True)
    hwnd = find_poe2_window()
    if not hwnd:
        print("[Cycle] Khoi chay POE2 Client qua Poe2ClientLauncher...", flush=True)
        ok = Poe2ClientLauncher.launch_client(wait_timeout_sec=90)
        if not ok:
            print("[Cycle Error] Khoi chay game client that bai!", flush=True)
            return {"status": "FAILED", "error": "Khoi chay game that bai"}
        start_find = time.time()
        while time.time() - start_find < 90:
            hwnd = find_poe2_window()
            if hwnd:
                print(f"[Cycle] 🎯 Đã tìm thấy HWND: 0x{hwnd:X} sau {time.time() - start_find:.1f}s", flush=True)
                break
            time.sleep(1.0)

    if not hwnd:
        print("[Cycle Error] Khong tim thay HWND sau launch!", flush=True)
        return {"status": "FAILED", "error": "Khong tim thay HWND sau launch"}

    already_in_hideout = False
    if hwnd:
        try:
            from src.assistant_tool.screen_capturer import ScreenCapturer
            import src.common.vision_ocr as vision_ocr
            from PIL import Image
            capturer = ScreenCapturer(output_dir="captures/pipeline_run")
            cap_path = capturer.capture_screenshot(event_name="PRE_CYCLE_CHECK", capture_ram=False, auto_focus=False, background_mode=True)
            if cap_path and os.path.exists(cap_path):
                img_chk = Image.open(cap_path)
                res_check = vision_ocr.safe_recognize_pil_sync(img_chk)
                txt_check = " ".join(l.get("text", "") for l in res_check.get("lines", [])).upper()
                is_login_screen = any(kw in txt_check for kw in ["LOG IN", "LOGIN", "SAVED LOGIN", "SELECT CHARACTER"])
                is_in_game = any(kw in txt_check for kw in ["WORLD", "LIFE", "SHIELD", "MAP", "WAYPOINT", "COSMETICS", "ENDGAME", "MANA", "SPIRIT", "QUESTS"])
                if not is_login_screen and is_in_game:
                    already_in_hideout = True
                    print(f"[Cycle] 🎯 Cảm biến quang học xác nhận nhân vật ĐANG Ở IN-GAME / HIDEOUT! (Tokens: {txt_check[:100]}...)", flush=True)
        except Exception as e:
            print(f"[Cycle Warning] Optical precheck: {e}", flush=True)

    if already_in_hideout:
        print("[Cycle] 🎯 Nhân vật ĐÃ Ở SẴN trong Shoreline Hideout! Bỏ qua Reconnect.", flush=True)
        rc_result = {
            "success": True,
            "zone": "Shoreline Hideout",
            "hwnd": hex(hwnd),
            "evidence_images": {},
            "vitals": {"current_hp": 1, "max_hp": 1, "current_shield": 4284, "max_shield": 4284},
            "skipped_reconnect": True,
        }
    else:
        print(f"[Cycle] HWND game: 0x{hwnd:X}. Bat dau kịch bản Reconnect đưa nhân vật vào Hideout...", flush=True)
        rc_result = reconnect_module.main(hwnd=hwnd)
        print(f"[Cycle] Ket qua Reconnect: {rc_result.get('success')}", flush=True)

        if not rc_result.get("success"):
            print(f"[Cycle Error] Reconnect that bai: {rc_result.get('error')}", flush=True)
            return {"status": "FAILED", "error": f"Reconnect that bai: {rc_result.get('error')}"}

    print("[Cycle] ✅ Monk CI da vao Shoreline Hideout an toan!", flush=True)
    print("[Cycle] Cho 5.0s on dinh moi truong Hideout truoc khi bat dau cay map...", flush=True)
    time.sleep(5.0)
    ensure_window_focus(hwnd, timeout_ms=1000)
    time.sleep(0.5)
    cl, ct, cw, ch = get_client_rect(hwnd)
    print(f"[Cycle] 🎯 Cửa sổ POE2 đã sẵn sàng tương tác (HWND=0x{hwnd:X}, ClientRect=({cl}, {ct}, {cw}x{ch}))", flush=True)

    # Kích hoạt DirectX 12 3D Viewport RawInput & xác thực khả năng nhận phím/chuột
    try:
        import src.common.win32_input as win32_input
        # 1. Click vào tâm trên 3D viewport để bind con trỏ chuột vào không gian 3D
        if cw > 200 and ch > 200:
            vp_x = cl + cw // 2
            vp_y = ct + ch // 3
            ctypes.windll.user32.SetCursorPos(vp_x, vp_y)
            time.sleep(0.04)
            ctypes.windll.user32.mouse_event(0x0002, 0, 0, 0, 0)
            time.sleep(0.03)
            ctypes.windll.user32.mouse_event(0x0004, 0, 0, 0, 0)
            time.sleep(0.1)

        # 2. Toggle Character Panel (phím 'C' 0x43) rồi đóng bằng Spacebar để kích hoạt engine input
        win32_input.press_key(0x43, 0x2E, hold_ms=0.05, delay_after=0.15)
        win32_input.press_key(0x20, 0x39, hold_ms=0.05, delay_after=0.2)
        print("[Cycle] 🎯 Đã kích hoạt thành công DirectX 12 3D Viewport & RawInput!", flush=True)
    except Exception as e_act:
        print(f"[Cycle Warning] Viewport activation: {e_act}", flush=True)

    # Đảm bảo đóng các dialog / modal tồn đọng bằng Spacebar chuẩn người chơi POE2
    try:
        import src.common.win32_input as win32_input
        win32_input.dismiss_modals(count=1, delay_after=0.2)
    except Exception as e_clean:
        print(f"[Cycle Warning] Modal dismiss: {e_clean}", flush=True)

    print(f"\n[Cycle] Buoc 2: Kich hoat Autonomous Mapping Coordinator (MapDuration={map_duration_sec}s, Stash={enable_stashing}, MaxMaps={max_maps})...", flush=True)
    coordinator = AutonomousMappingCoordinator(
        screen_w=cw,
        screen_h=ch,
        map_duration_sec=map_duration_sec,
        min_map_duration_sec=min(90.0, float(map_duration_sec)),
        capture_dir="captures/pipeline_run",
        auto_detect_resolution=False,
    )
    coordinator.hwnd = hwnd
    coordinator.client_left = cl
    coordinator.client_top = ct

    if max_maps > 1:
        report = coordinator.run_multi_map_loop(
            max_maps=max_maps,
            enable_stashing=enable_stashing,
            force_new_map=force_new_map,
        )
    else:
        report = coordinator.execute_pipeline(
            target_slot_idx=0,
            enable_stashing=enable_stashing,
            enable_post_stashing=enable_stashing,
            allow_portal_bypass=(not force_new_map),
        )

    # Thu thập artifact thực tế: log Client.txt và telemetry RAM dump (DoD Rule 5)
    report["reconnect_data"] = rc_result
    tail_lines = []
    if os.path.exists(client_log_path):
        try:
            with open(client_log_path, "r", encoding="utf-8", errors="ignore") as f:
                f.seek(start_log_offset)
                tail_lines = [line.strip() for line in f.readlines() if line.strip()]
        except Exception as e:
            print(f"[Cycle Warning] Không thể đọc tail Client.txt: {e}", flush=True)
    report["client_log_tail"] = tail_lines

    # Thu thập RAM dump files
    ram_dump_files = []
    for cap_dir in ["captures/pipeline_run", "captures/reconnect"]:
        p_dir = Path(cap_dir)
        if p_dir.exists():
            for f in p_dir.glob("*_RAM.json"):
                try:
                    if f.stat().st_mtime >= cycle_start_time - 5.0:
                        ram_dump_files.append(str(f.resolve()))
                except Exception:
                    pass
    report["ram_dump_files"] = ram_dump_files

    print("\n" + "=" * 66, flush=True)
    print("                      KET QUA VAN HANH TOAN TRINH                 ", flush=True)
    print("=" * 66, flush=True)
    print(f"Trang thai: {report.get('status')}", flush=True)
    print(f"Stages hoan thanh: {report.get('stages_completed')}", flush=True)
    print(f"Tong so anh bang chung: {len(report.get('evidence_images', []))}", flush=True)
    print(f"Tong so anh Sentinel 3s: {len(report.get('sentinel_images', []))}", flush=True)
    print(f"Tong so file RAM dump: {len(report.get('ram_dump_files', []))}", flush=True)
    print(f"So dong Client.txt thu thap: {len(report.get('client_log_tail', []))}", flush=True)
    if report.get("error"):
        print(f"Loi phat sinh: {report.get('error')}", flush=True)

    out_json = "debug_harness/live_ops_cycle_result.json"
    os.makedirs(os.path.dirname(out_json), exist_ok=True)
    with open(out_json, "w", encoding="utf-8") as f:
        json.dump(report, f, indent=2, ensure_ascii=False)
    print(f"\nDa luu ket qua toan trinh vao: {out_json}", flush=True)

    return report


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="AutoPOE2 Live Ops Full Autonomous Mapping Cycle")
    parser.add_argument("--map-duration", type=int, default=100, help="Thời lượng in-map clear (giây, 90s-240s, mặc định: 100)")
    parser.add_argument("--enable-stashing", action="store_true", default=False, help="Bật cất đồ Stash (Stage 0)")
    parser.add_argument("--max-maps", type=int, default=1, help="Số map chạy liên tục (mặc định: 1)")
    parser.add_argument("--force-new-map", action="store_true", default=False, help="Bắt buộc mở map mới, không dùng lại portal cũ")
    parser.add_argument("--no-kmbox", action="store_true", default=True, help="Vận hành 100% thuần phần mềm, không phụ thuộc phần cứng KMBox (mặc định: True)")
    args = parser.parse_args()

    try:
        main(
            map_duration_sec=args.map_duration,
            enable_stashing=args.enable_stashing,
            max_maps=args.max_maps,
            force_new_map=args.force_new_map,
            no_kmbox=args.no_kmbox,
        )
    except Exception as e:
        import traceback
        print(f"\n[FATAL ERROR] {e}", flush=True)
        traceback.print_exc()
        sys.exit(1)

