# -*- coding: utf-8 -*-
"""
STEP114 Publish Engine V2

목표:
- 기존 운영 publish_worker.py는 그대로 둔다.
- 신규 플랫폼용 발행 엔진의 "껍데기"부터 만든다.
- queue_id 기준으로 발행 전 점검 → Human Profile 확인 → STEP105 wrapper 호출 → 결과 확인까지 담당한다.
- 실제 Playwright 발행 로직은 workers.v2_publish_worker가 수행한다.
- V2 1차 운영에서는 publish_engine이 모든 발행 경로의 관문 역할을 담당한다.
"""

import sys
import json
import time
import subprocess
from pathlib import Path
from datetime import datetime

BASE_DIR = Path(__file__).resolve().parents[1]
if str(BASE_DIR) not in sys.path:
    sys.path.append(str(BASE_DIR))

from db import get_conn

try:
    from workers.publish_modules.human_profile import get_human_profile, profile_summary
except Exception:
    get_human_profile = None
    profile_summary = None


PYTHON_EXE = sys.executable


def log(*args):
    print(*args, flush=True)


def clean_text(value):
    return str(value or "").strip()


def safe_json(row):
    return json.dumps(row or {}, ensure_ascii=False, default=str)


def fetch_queue(conn, queue_id):
    with conn.cursor() as cur:
        cur.execute("""
            SELECT *
            FROM blog_publish_queue
            WHERE id = %s
            LIMIT 1
        """, (int(queue_id),))
        row = cur.fetchone()

    if not row:
        raise RuntimeError(f"publish queue not found: {queue_id}")

    return row


def fetch_draft(conn, draft_id):
    if not draft_id:
        return {}

    with conn.cursor() as cur:
        cur.execute("""
            SELECT *
            FROM blog_article_drafts
            WHERE id = %s
            LIMIT 1
        """, (int(draft_id),))
        return cur.fetchone() or {}


def fetch_realtor(conn, realtor_id):
    with conn.cursor() as cur:
        cur.execute("""
            SELECT *
            FROM blog_realtors
            WHERE id = %s
            LIMIT 1
        """, (int(realtor_id),))
        row = cur.fetchone()

    if not row:
        raise RuntimeError(f"realtor not found: {realtor_id}")

    return row


def fetch_publish_setting(conn, realtor_id):
    try:
        with conn.cursor() as cur:
            cur.execute("""
                SELECT *
                FROM blog_publish_settings
                WHERE realtor_id = %s
                LIMIT 1
            """, (int(realtor_id),))
            return cur.fetchone() or {}
    except Exception:
        return {}


def load_publish_context(queue_id):
    conn = get_conn()
    try:
        queue = fetch_queue(conn, queue_id)
        realtor = fetch_realtor(conn, queue["realtor_id"])
        draft = fetch_draft(conn, queue.get("draft_id"))
        setting = fetch_publish_setting(conn, queue["realtor_id"])

        return {
            "queue": queue,
            "realtor": realtor,
            "draft": draft,
            "setting": setting,
        }
    finally:
        try:
            conn.close()
        except Exception:
            pass


def validate_publish_context(ctx, require_test_account=False):
    queue = ctx["queue"]
    realtor = ctx["realtor"]
    draft = ctx.get("draft") or {}

    errors = []
    warnings = []

    queue_status = clean_text(queue.get("queue_status"))
    if queue_status not in {"pending", "waiting", "ready", "failed"}:
        errors.append(f"queue_status not publishable: {queue_status}")

    if not queue.get("draft_id"):
        errors.append("draft_id empty")

    clipboard_html = clean_text(draft.get("clipboard_html") or draft.get("draft_html"))
    if not clipboard_html:
        errors.append("draft clipboard_html/draft_html empty")

    if not clean_text(queue.get("article_no")):
        warnings.append("article_no empty")

    publish_mode = clean_text(realtor.get("publish_mode") or "")
    try:
        is_test_account = int(realtor.get("is_test_account") or 0)
    except Exception:
        is_test_account = 0

    if require_test_account and not (is_test_account == 1 or publish_mode == "test"):
        errors.append("require_test_account enabled but realtor is not test")

    return {
        "ok": not errors,
        "errors": errors,
        "warnings": warnings,
    }


def get_context_human_profile(ctx):
    realtor = ctx.get("realtor") or {}
    realtor_id = realtor.get("id")

    if not get_human_profile:
        return {
            "available": False,
            "profile": None,
            "summary": None,
        }

    profile = get_human_profile(realtor_id=realtor_id)
    summary = profile_summary(profile) if profile_summary else profile

    return {
        "available": True,
        "profile": profile,
        "summary": summary,
    }


def run_publish_wrapper(queue_id, require_test_account=True, hpe_mode="test", execute=True):
    """
    STEP114:
    V2 Publish Worker를 표준 발행 경로로 호출한다.

    require_test_account는 publish_engine의 사전 검증에서 처리한다.
    v2_publish_worker는 queue_id 기준으로 queue claim, 발행, DB update까지 수행한다.
    """
    cmd = [
        PYTHON_EXE,
        "-m",
        "workers.v2_publish_worker",
        "--queue-id",
        str(int(queue_id)),
        "--hpe-mode",
        str(hpe_mode or "test"),
    ]

    if execute:
        cmd.append("--execute")
    else:
        cmd.append("--check-final-publish")

    log("=" * 80)
    log("[STEP114 V2 WORKER CALL]")
    log("[CMD]", " ".join(cmd))
    log("=" * 80)

    started = time.time()

    # stdout/stderr를 캡처하지 않고 자식 프로세스가 직접 콘솔에 출력하게 둔다.
    proc = subprocess.run(
        cmd,
        cwd=str(BASE_DIR),
        timeout=4500,
    )

    elapsed = time.time() - started

    log("=" * 80)
    log(f"[STEP114 V2 WORKER DONE] returncode={proc.returncode} elapsed={elapsed:.1f}s")
    log("=" * 80)

    if proc.returncode != 0:
        raise RuntimeError(f"v2 publish worker failed: {proc.returncode}")

    return {
        "returncode": proc.returncode,
        "elapsed": elapsed,
        "worker": "workers.v2_publish_worker",
        "hpe_mode": hpe_mode,
        "execute": execute,
    }


def publish_queue(queue_id, require_test_account=False, dry_run=False, hpe_mode="test"):
    ctx = load_publish_context(queue_id)
    validation = validate_publish_context(ctx, require_test_account=require_test_account)
    human = get_context_human_profile(ctx)

    log("=" * 80)
    log("[STEP114 V2 PUBLISH ENGINE START]")
    log("TIME:", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    log("queue_id:", queue_id)
    log("hpe_mode:", hpe_mode)
    log("realtor_id:", ctx["queue"].get("realtor_id"))
    log("office_name:", ctx["realtor"].get("office_name"))
    log("article_no:", ctx["queue"].get("article_no"))
    log("queue_status:", ctx["queue"].get("queue_status"))
    log("draft_id:", ctx["queue"].get("draft_id"))
    log("human_profile:", safe_json(human.get("summary")))
    log("validation:", safe_json(validation))
    log("=" * 80)

    if not validation["ok"]:
        raise RuntimeError("publish context validation failed: " + safe_json(validation))

    if dry_run:
        log("[DRY RUN] publish skipped")
        return {
            "ok": True,
            "dry_run": True,
            "context": ctx,
            "validation": validation,
            "human": human,
        }

    wrapper_result = run_publish_wrapper(queue_id, require_test_account=require_test_account, hpe_mode=hpe_mode, execute=True)

    final_ctx = load_publish_context(queue_id)
    final_queue = final_ctx["queue"]

    log("[STEP114 FINAL QUEUE]", safe_json(final_queue))
    log("=" * 80)
    log("[STEP114 V2 PUBLISH ENGINE DONE]")
    log("=" * 80)

    return {
        "ok": True,
        "dry_run": False,
        "queue": final_queue,
        "wrapper": wrapper_result,
    }
