import os
import time
from datetime import datetime

os.environ["FLAGS_enable_pir_api"] = "0"
os.environ["FLAGS_use_onednn"] = "0"
os.environ["FLAGS_use_mkldnn"] = "0"
os.environ["FLAGS_use_onednn_bfloat16"] = "0"
os.environ["MKLDNN_DISABLE_WORKSPACE"] = "1"
os.environ["ONEDNN_VERBOSE"] = "0"

import pyperclip
from paddleocr import PaddleOCR
from PIL import Image
from PIL import ImageFilter
from PIL import ImageOps

from common.config_loader import loadOcrConfig
from common.runtime_log import appendRuntimeLog
from screen.screen_actions import captureOcrRegion

_ocr_engine = None


def log(level, message):
    now = datetime.now().strftime("%H:%M:%S")
    print(f"[{level}] {now} {message}", flush=True)


def getOcrEngine():
    global _ocr_engine

    if _ocr_engine is None:
        log("INFO", "PaddleOCR initializing")
        config = loadOcrConfig()
        options = {
            "lang": config["lang"],
            "use_angle_cls": config["use_angle_cls"],
            "show_log": config["show_log"],
        }

        try:
            _ocr_engine = PaddleOCR(**options)
        except (TypeError, ValueError):
            options.pop("show_log", None)
            _ocr_engine = PaddleOCR(**options)

    return _ocr_engine


_gemini_client = None


def getGeminiClient(api_key):
    global _gemini_client

    if _gemini_client is not None:
        return _gemini_client

    if not api_key:
        raise RuntimeError("Gemini API key is not set in secrets.toml [ai]")

    try:
        from google import genai
    except Exception as e:
        raise RuntimeError(f"Gemini SDK is missing detail={e}")

    _gemini_client = genai.Client(api_key=api_key)
    return _gemini_client


def saveOcrText(image_path, text):
    return appendRuntimeLog(
        "OCR TEXT",
        f"image={image_path}\n{text}",
    )


def extractTextFromAny(obj):
    lines = []

    def walk(x):
        if x is None:
            return

        if isinstance(x, dict):
            for key in ("rec_texts", "texts"):
                value = x.get(key)
                if isinstance(value, list):
                    for item in value:
                        text = str(item).strip()
                        if text:
                            lines.append(text)

            for key in ("text", "rec_text"):
                value = x.get(key)
                if isinstance(value, str) and value.strip():
                    lines.append(value.strip())

            for value in x.values():
                walk(value)
            return

        if isinstance(x, (list, tuple)):
            if len(x) >= 2 and isinstance(x[1], (list, tuple)) and len(x[1]) >= 1:
                candidate = x[1][0]
                if isinstance(candidate, str) and candidate.strip():
                    lines.append(candidate.strip())

            for item in x:
                walk(item)

    walk(obj)

    unique_lines = []
    seen = set()
    for line in lines:
        if line not in seen:
            unique_lines.append(line)
            seen.add(line)

    return "\n".join(unique_lines)


def printOcrResult(text):
    cleaned = text.strip()
    if not cleaned:
        log("INFO", "OCR結果は空です")
        return

    log("CHECK", "OCR結果 full")
    for line in cleaned.splitlines():
        print(line, flush=True)


def preprocessOcrImage(image_path):
    config = loadOcrConfig()
    enabled = config["preprocess"]
    if not enabled:
        return image_path

    try:
        image = Image.open(image_path)
        image = image.convert("L")
        image = ImageOps.autocontrast(image)
        image = image.filter(ImageFilter.SHARPEN)

        scale = max(1.0, min(2.0, config["scale"]))
        if scale > 1.0:
            width, height = image.size
            resampling = getattr(
                getattr(Image, "Resampling", Image),
                "LANCZOS",
            )
            image = image.resize(
                (int(width * scale), int(height * scale)),
                resampling,
            )

        image.save(image_path)
        log("INFO", f"OCR image preprocessed={image_path}")
    except Exception as e:
        log("ERROR", f"OCR preprocess failed detail={e}")

    return image_path


def runPaddleOcr(engine, image_path):
    if hasattr(engine, "predict"):
        return engine.predict(str(image_path))
    return engine.ocr(str(image_path))


def runGeminiOcr(image_path):
    config = loadOcrConfig()
    model_name = config["model_name"] or "gemini-2.5-flash"
    prompt = (
        "Extract every readable text fragment from this image with maximum accuracy. "
        "Do not summarize, explain, translate, or guess. "
        "Return only the text exactly as it appears, preserving line breaks and order."
    )

    try:
        from google.genai import types
    except Exception as e:
        raise RuntimeError(f"Gemini types import failed detail={e}")

    client = getGeminiClient(config["api_key"])
    mime_type = "image/png"
    suffix = str(image_path).lower()
    if suffix.endswith(".jpg") or suffix.endswith(".jpeg"):
        mime_type = "image/jpeg"
    elif suffix.endswith(".webp"):
        mime_type = "image/webp"

    image_bytes = image_path.read_bytes()
    image_part = types.Part.from_bytes(data=image_bytes, mime_type=mime_type)
    response = client.models.generate_content(
        model=model_name,
        contents=[prompt, image_part],
    )
    text = (response.text or "").strip()
    return text


def runOcrFromImage(image_path, show_result=True):
    try:
        image_path = preprocessOcrImage(image_path)
        config = loadOcrConfig()
        provider = config["provider"]
        log("ACTION", f"OCR start provider={provider} image={image_path}")

        if provider == "gemini":
            text = runGeminiOcr(image_path)
        else:
            engine = getOcrEngine()
            result = runPaddleOcr(engine, image_path)
            text = extractTextFromAny(result)

        if not text:
            raise RuntimeError("OCR text is empty")
    except Exception as e:
        log("ERROR", f"OCR failed detail={e}")

        if config.get("provider") == "gemini":
            log("INFO", "Falling back to PaddleOCR")
            try:
                engine = getOcrEngine()
                result = runPaddleOcr(engine, image_path)
                text = extractTextFromAny(result)
            except Exception as fallback_error:
                log("ERROR", f"PaddleOCR fallback failed detail={fallback_error}")
                return None
        else:
            return None

    text_path = saveOcrText(image_path, text)

    try:
        pyperclip.copy(text)
        log("INFO", "OCR結果をクリップボードへコピーしました")
    except Exception as e:
        log("ERROR", f"clipboard copy failed detail={e}")

    log("INFO", f"OCR text appended={text_path}")
    if show_result:
        printOcrResult(text)

    return {
        "image_path": image_path,
        "text_path": text_path,
        "text": text,
    }


def runOcrFromScreen():
    log("ACTION", "runOcrFromScreen called")
    image_path = captureOcrRegion()
    delay_seconds = max(0.0, loadOcrConfig().get("delay_seconds", 1.0))
    if delay_seconds:
        log("INFO", f"OCR delayed seconds={delay_seconds}")
        time.sleep(delay_seconds)
    return runOcrFromImage(image_path, show_result=True)
