#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
지식인 AI 답변기 - 데스크톱 클립보드 모니터
----------------------------------------------
복사만 하면 자동으로 AI 답변 생성 → 알림 → 클립보드 복사

설치: pip install -r requirements.txt
실행: python jisikinator_desktop.py
exe 빌드: pyinstaller --onefile --windowed --name "지식인AI답변기" jisikinator_desktop.py
"""

import sys
import os
import time
import json
import threading
import requests
import pyperclip

# Windows에서 UTF-8 인코딩 강제 (콘솔 출력/클립보드 모두 한글 안깨짐)
if sys.platform == "win32":
    try:
        import ctypes
        # Set process codepage to UTF-8 (Windows 10 1903+)
        ctypes.windll.kernel32.SetConsoleCP(65001)
        ctypes.windll.kernel32.SetConsoleOutputCP(65001)
    except Exception:
        pass

# ─── 설정 (여기서 URL 변경) ────────────────────────────────────────────────────
API_URL = "https://ai-answer-bot-spokduzve.replit.app/api"   # 서버 URL (자동 설정됨)
DEFAULT_STYLE = "친절한"   # 친절한 / 전문가 / 간단명료
MIN_LENGTH = 15            # 최소 글자 수 (이보다 짧으면 무시)
# ──────────────────────────────────────────────────────────────────────────────

APP_NAME = "지식인 AI 답변기"
is_generating = False
current_style = DEFAULT_STYLE


def fix_mojibake(text):
    """
    혹시 이미 깨진 UTF-8 문자열이 latin1으로 디코딩된 상태라면 복구합니다.
    API/클립보드 사이에서 인코딩이 꼬였을 때 최후의 복구 수단.
    """
    for _ in range(4):
        if "Ã" in text or "â" in text or "ï¿½" in text:
            try:
                fixed = text.encode("latin1", "replace").decode("utf-8", "replace")
                if fixed == text:
                    break
                text = fixed
            except Exception:
                break
        else:
            break
    return text


def notify(title, message, timeout=5):
    try:
        from plyer import notification
        notification.notify(
            title=title,
            message=message,
            app_name=APP_NAME,
            timeout=timeout,
        )
    except Exception as e:
        print(f"[알림 오류] {e}")


def generate_answer(question):
    """SSE 스트리밍으로 AI 답변 생성"""
    full_answer = ""
    try:
        with requests.post(
            f"{API_URL}/answers/stream",
            json={"question": question, "style": current_style},
            stream=True,
            timeout=60,
            headers={"Accept": "text/event-stream"},
        ) as resp:
            if resp.status_code != 200:
                return None, f"API 오류: {resp.status_code}"

            # Read raw bytes and decode as UTF-8 explicitly to avoid
            # Windows/latin1 mojibake issues.
            for raw_line in resp.iter_lines():
                line = raw_line.decode("utf-8", errors="replace") if isinstance(raw_line, bytes) else raw_line
                if line and line.startswith("data: "):
                    try:
                        data = json.loads(line[6:])
                        if "content" in data:
                            full_answer += data["content"]
                        elif data.get("done"):
                            break
                        elif "error" in data:
                            return None, data["error"]
                    except json.JSONDecodeError:
                        continue

        # Apply mojibake recovery just in case the content was double-encoded
        full_answer = fix_mojibake(full_answer)
        return (full_answer.strip() or None), None

    except requests.exceptions.ConnectionError:
        return None, "서버 연결 실패. API_URL을 확인하세요."
    except requests.exceptions.Timeout:
        return None, "요청 시간 초과."
    except Exception as e:
        return None, str(e)


def process_clip(text):
    """클립보드 텍스트를 처리하여 답변 생성"""
    global is_generating

    text = text.strip()
    if len(text) < MIN_LENGTH:
        return
    if is_generating:
        return

    is_generating = True
    try:
        print(f"\n[질문 감지] {text[:60]}...")
        notify(APP_NAME, "답변 생성 중...", timeout=2)

        answer, error = generate_answer(text)

        if error:
            print(f"[오류] {error}")
            notify(f"{APP_NAME} - 오류", error, timeout=5)
            return

        if answer:
            pyperclip.copy(answer)
            print(f"[완료] 답변 {len(answer)}자 → 클립보드 복사됨")
            notify(
                f"{APP_NAME} - 답변 완료",
                "Ctrl+V 로 바로 붙여넣기 하세요!",
                timeout=8,
            )
        else:
            notify(APP_NAME, "빈 답변이 생성됐습니다.", timeout=3)

    finally:
        is_generating = False


def clipboard_monitor():
    """클립보드 변화 감지 루프"""
    last = ""
    print("[모니터링 시작] 텍스트를 복사하면 자동으로 답변을 생성합니다.")
    notify(APP_NAME, f"백그라운드 실행 중\n스타일: {current_style}", timeout=3)

    while True:
        try:
            text = pyperclip.paste()
            if text and text != last:
                last = text
                threading.Thread(target=process_clip, args=(text,), daemon=True).start()
        except Exception as e:
            print(f"[클립보드 오류] {e}")
        time.sleep(0.5)


def run_with_tray():
    """시스템 트레이 아이콘과 함께 실행"""
    try:
        import pystray
        from PIL import Image, ImageDraw, ImageFont

        def make_icon():
            img = Image.new("RGBA", (64, 64), (0, 0, 0, 0))
            d = ImageDraw.Draw(img)
            d.ellipse([2, 2, 62, 62], fill=(20, 184, 166))
            d.text((16, 18), "AI", fill=(255, 255, 255))
            return img

        def set_style(s):
            def _fn(icon, item):
                global current_style
                current_style = s
                notify(APP_NAME, f"스타일 변경: {s}", timeout=2)
            return _fn

        def quit_app(icon, item):
            icon.stop()
            sys.exit(0)

        menu = pystray.Menu(
            pystray.MenuItem(APP_NAME, None, enabled=False),
            pystray.Menu.SEPARATOR,
            pystray.MenuItem("스타일: 친절한", set_style("친절한")),
            pystray.MenuItem("스타일: 전문가", set_style("전문가")),
            pystray.MenuItem("스타일: 간단명료", set_style("간단명료")),
            pystray.Menu.SEPARATOR,
            pystray.MenuItem("종료", quit_app),
        )

        threading.Thread(target=clipboard_monitor, daemon=True).start()
        icon = pystray.Icon(APP_NAME, make_icon(), APP_NAME, menu)
        icon.run()

    except ImportError:
        # pystray 없으면 트레이 없이 실행
        clipboard_monitor()


if __name__ == "__main__":
    print(f"=== {APP_NAME} ===")
    print(f"API: {API_URL}")
    print(f"스타일: {current_style}  |  최소 글자: {MIN_LENGTH}")
    print("종료: Ctrl+C\n")
    try:
        run_with_tray()
    except KeyboardInterrupt:
        print("\n종료합니다.")
        sys.exit(0)
