"""
AutoPOE2 - Isolated WinOCR Worker & Client (Doc 50 / Rule 1 / Rule 6)
=====================================================================
Chạy WinOCR bên trong một tiến trình con cô lập (Isolated Subprocess).
Khắc phục triệt để lỗi Windows SEH Access Violation 0xc0000005 trong
_winrt_windows_storage_streams.cp311-win_amd64.pyd, đảm bảo tiến trình chính
(Coordinator / Game Loop) không bao giờ bị sập (Crash-Proof 100%).
"""

import os
import sys
import json
import time
import atexit
import tempfile
import threading
import subprocess
from typing import Any, Dict, Optional
from PIL import Image


class IsolatedOcrWorker:
    """Entry point chạy bên trong tiến trình con worker."""

    @staticmethod
    def run():
        try:
            if hasattr(sys.stdout, "reconfigure"):
                sys.stdout.reconfigure(encoding="utf-8")
            if hasattr(sys.stdin, "reconfigure"):
                sys.stdin.reconfigure(encoding="utf-8")
        except Exception:
            pass

        import winocr

        while True:
            line = sys.stdin.readline()
            if not line:
                break
            line_str = line.strip()
            if not line_str:
                continue

            try:
                req = json.loads(line_str)
                path = req.get("path")
                lang = req.get("lang", "en")
                if path and os.path.exists(path):
                    with Image.open(path) as img:
                        res = winocr.recognize_pil_sync(img, lang=lang)
                else:
                    res = {"lines": [], "text": "", "error": "file_not_found"}
            except Exception as e:
                res = {"lines": [], "text": "", "error": str(e)}

            try:
                out_line = json.dumps(res, ensure_ascii=False)
                sys.stdout.write(out_line + "\n")
                sys.stdout.flush()
            except Exception:
                pass


class IsolatedOcrClient:
    """Client luồng đơn/đa luồng giao tiếp với IsolatedOcrWorker qua Stdin/Stdout pipes."""

    _instance = None
    _lock = threading.Lock()

    def __init__(self):
        self._proc: Optional[subprocess.Popen] = None
        self._proc_lock = threading.Lock()
        self._temp_dir = os.path.join(tempfile.gettempdir(), "autopoe2_ocr_temp")
        os.makedirs(self._temp_dir, exist_ok=True)
        self._req_counter = 0

    @classmethod
    def get_instance(cls) -> "IsolatedOcrClient":
        with cls._lock:
            if cls._instance is None:
                cls._instance = IsolatedOcrClient()
                atexit.register(cls._instance.shutdown)
            return cls._instance

    def _ensure_proc(self) -> bool:
        if self._proc is not None and self._proc.poll() is None:
            return True

        try:
            cmd = [sys.executable, "-m", "src.common.winocr_worker"]
            c_flags = subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0
            self._proc = subprocess.Popen(
                cmd,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.DEVNULL,
                text=True,
                encoding="utf-8",
                cwd=str(os.path.abspath(".")),
                creationflags=c_flags,
            )
            return True
        except Exception as e:
            print(f"[IsolatedOcrClient Error] Không thể khởi chạy worker: {e}", flush=True)
            self._proc = None
            return False

    def recognize_pil_sync(self, img: Image.Image, lang: str = "en") -> Dict[str, Any]:
        """Nhận diện PIL Image thông qua tiến trình con cô lập (Crash-Proof)."""
        if img is None:
            return {"lines": [], "text": ""}

        with self._proc_lock:
            if not self._ensure_proc():
                return {"lines": [], "text": "", "error": "worker_unavailable"}

            self._req_counter += 1
            temp_path = os.path.join(self._temp_dir, f"ocr_{os.getpid()}_{self._req_counter}.png")
            try:
                img.save(temp_path, "PNG")
                req_json = json.dumps({"path": temp_path, "lang": lang})
                self._proc.stdin.write(req_json + "\n")
                self._proc.stdin.flush()

                resp_line = self._proc.stdout.readline()
                if not resp_line:
                    # Worker process đã bị crash hoặc terminated -> Khởi động lại
                    self._proc = None
                    return {"lines": [], "text": "", "error": "worker_terminated"}

                return json.loads(resp_line.strip())
            except Exception as e:
                self._proc = None
                return {"lines": [], "text": "", "error": str(e)}
            finally:
                try:
                    if os.path.exists(temp_path):
                        os.remove(temp_path)
                except Exception:
                    pass

    def recognize_path_sync(self, path: str, lang: str = "en") -> Dict[str, Any]:
        """Nhận diện ảnh từ đường dẫn file trực tiếp."""
        if not path or not os.path.exists(path):
            return {"lines": [], "text": "", "error": "file_not_found"}

        with self._proc_lock:
            if not self._ensure_proc():
                return {"lines": [], "text": "", "error": "worker_unavailable"}

            try:
                req_json = json.dumps({"path": os.path.abspath(path), "lang": lang})
                self._proc.stdin.write(req_json + "\n")
                self._proc.stdin.flush()

                resp_line = self._proc.stdout.readline()
                if not resp_line:
                    self._proc = None
                    return {"lines": [], "text": "", "error": "worker_terminated"}

                return json.loads(resp_line.strip())
            except Exception as e:
                self._proc = None
                return {"lines": [], "text": "", "error": str(e)}

    def shutdown(self):
        with self._proc_lock:
            if self._proc is not None:
                try:
                    self._proc.stdin.close()
                    self._proc.terminate()
                    self._proc.wait(timeout=1.0)
                except Exception:
                    pass
                self._proc = None


# Tiện ích hàm ngoài dùng trực tiếp
def safe_ocr_pil(img: Image.Image, lang: str = "en") -> Dict[str, Any]:
    return IsolatedOcrClient.get_instance().recognize_pil_sync(img, lang=lang)

def safe_ocr_path(path: str, lang: str = "en") -> Dict[str, Any]:
    return IsolatedOcrClient.get_instance().recognize_path_sync(path, lang=lang)


if __name__ == "__main__":
    IsolatedOcrWorker.run()
