# -*- coding: utf-8 -*-
"""
STEP111-11 HEE Publish-Always Layout Policy v1

운영 원칙:
- 네이버부동산에 정상 등록/수집된 매물은 발행 대상이다.
- 사진/설명/학군/평면도 부족은 발행 차단 사유가 아니다.
- 부족한 정보는 레이아웃을 단순화하는 기준으로만 사용한다.
- 단, 매물유형 불일치(아파트인데 토지 본문), 법정표시 누락 같은 운영 위험은 차단한다.

사용:
  cd /d D:\honghee\blog_api

  draft 기준:
  python tools\hee_publish_always_policy.py --draft-id 209

  article 기준:
  python tools\hee_publish_always_policy.py --realtor-id 5 --article-no 2633005438

  JSON:
  python tools\hee_publish_always_policy.py --realtor-id 5 --article-no 2633005438 --json
"""

import argparse
import json
import re
import sys
from pathlib import Path
from html import unescape

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))

from db import get_conn


def clean_text(value):
    value = "" if value is None else str(value)
    value = unescape(value)
    value = re.sub(r"<[^>]+>", " ", value)
    value = re.sub(r"\s+", " ", value)
    return value.strip()


def safe_json_loads(value):
    if not value:
        return {}
    if isinstance(value, dict):
        return value
    try:
        return json.loads(value)
    except Exception:
        return {}


def fetch_draft(conn, draft_id=None, realtor_id=None, article_no=None):
    with conn.cursor() as cur:
        if draft_id:
            cur.execute("SELECT * FROM blog_article_drafts WHERE id=%s LIMIT 1", (int(draft_id),))
            return cur.fetchone()

        cur.execute(
            """
            SELECT *
            FROM blog_article_drafts
            WHERE realtor_id=%s AND article_no=%s
            ORDER BY id DESC
            LIMIT 1
            """,
            (int(realtor_id), str(article_no)),
        )
        return cur.fetchone()


def fetch_article(conn, realtor_id, article_no):
    with conn.cursor() as cur:
        cur.execute(
            """
            SELECT *
            FROM blog_realtor_articles
            WHERE realtor_id=%s AND article_no=%s
            LIMIT 1
            """,
            (int(realtor_id), str(article_no)),
        )
        return cur.fetchone()


def extract_html(draft):
    return str(
        draft.get("clipboard_html")
        or draft.get("draft_html")
        or draft.get("content_html")
        or ""
    )


def count_images_from_html(html):
    return len(re.findall(r"<img\b", html or "", flags=re.I))


def extract_source_json(draft):
    return safe_json_loads(draft.get("source_json"))


def detect_property_from_article(article, draft=None):
    source = extract_source_json(draft or {}) if draft else {}
    detail = source.get("detail") or {}
    raw = safe_json_loads((article or {}).get("raw_json") or detail.get("raw_json"))

    article_detail = raw.get("articleDetail") or {}
    article_addition = raw.get("articleAddition") or {}

    type_name = clean_text(
        (article or {}).get("real_estate_type")
        or detail.get("real_estate_type")
        or detail.get("real_estate_type_name")
        or article_detail.get("realestateTypeName")
        or article_addition.get("articleRealEstateTypeName")
        or article_addition.get("realEstateTypeName")
    )

    type_code = clean_text(
        article_detail.get("articleTypeCode")
        or article_detail.get("realestateTypeCode")
        or article_addition.get("articleRealEstateTypeCode")
        or article_addition.get("realEstateTypeCode")
    ).upper()

    if type_name in ["아파트"] or type_code in ["A01", "APT"]:
        return "apartment"
    if type_name in ["오피스텔"]:
        return "officetel"
    if type_name in ["빌라", "연립", "다세대", "주택"]:
        return "villa_house"
    if type_name in ["상가", "상가점포"]:
        return "store"
    if type_name in ["사무실"]:
        return "office"
    if type_name in ["공장", "창고"]:
        return "factory_warehouse"
    if type_name in ["토지", "대", "전", "답", "임야", "대지", "잡종지", "공장용지", "창고용지"] or type_code in ["TJ", "LAND", "TOJI", "E03"]:
        return "land"
    if type_name in ["분양권", "입주권"]:
        return "presale_right"

    return "unknown"


def get_broker_desc_len(article, draft):
    source = extract_source_json(draft or {})
    detail = source.get("detail") or {}
    raw = safe_json_loads((article or {}).get("raw_json") or detail.get("raw_json"))
    article_detail = raw.get("articleDetail") or {}

    desc = clean_text(
        (article or {}).get("article_feature_desc")
        or detail.get("article_feature_desc")
        or article_detail.get("detailDescription")
        or article_detail.get("articleFeatureDescription")
    )
    return len(desc)


def source_image_count(draft, html):
    source = extract_source_json(draft or {})
    images = source.get("images") or []
    if images:
        return len(images)

    total = count_images_from_html(html)
    return max(0, total - 3)


def has_text(html, *words):
    text = clean_text(html)
    return any(w in text for w in words)


def choose_layout_mode(property_class, image_count, broker_desc_len, html):
    has_floorplan = has_text(html, "평면도", "구조")
    has_school = has_text(html, "학교", "학군", "초등학교", "중학교", "고등학교")

    if image_count <= 0 and broker_desc_len < 20:
        base = "minimal"
    elif image_count <= 1 or broker_desc_len < 40:
        base = "compact"
    elif image_count <= 4 or broker_desc_len < 80:
        base = "simple"
    elif image_count <= 10:
        base = "balanced"
    else:
        base = "rich"

    if property_class == "land":
        return f"land_{base}"
    if property_class in ["factory_warehouse"]:
        return f"factory_{base}"
    if property_class in ["store", "office"]:
        return f"business_{base}"
    if property_class in ["apartment", "officetel", "villa_house", "presale_right"]:
        if base in ["rich", "balanced"] and (has_floorplan or has_school):
            return f"living_{base}"
        return f"building_{base}"

    return f"generic_{base}"


def detect_critical_mismatch(property_class, html):
    text = clean_text(html)

    land_words = [
        "토지 개요",
        "토지 핵심 정보",
        "토지 확인 포인트",
        "토지 매물",
        "토지면적",
        "접도",
        "토지이용계획",
        "건축허가",
        "활용 가능성",
        "투자 검토 포인트",
    ]

    building_classes = ["apartment", "officetel", "villa_house", "store", "office", "factory_warehouse", "presale_right"]

    if property_class in building_classes:
        bad = [w for w in land_words if w in text]
        if bad:
            return {
                "critical": True,
                "reason": "building_property_has_land_story",
                "bad_words": bad,
            }

    return {
        "critical": False,
        "reason": "",
        "bad_words": [],
    }


def evaluate_policy(draft, article):
    html = extract_html(draft)
    property_class = detect_property_from_article(article, draft)
    image_count = source_image_count(draft, html)
    broker_desc_len = get_broker_desc_len(article, draft)
    layout_mode = choose_layout_mode(property_class, image_count, broker_desc_len, html)
    mismatch = detect_critical_mismatch(property_class, html)

    has_legal = has_text(html, "중개대상물 표시", "중개대상물 명시사항")
    has_realtor = has_text(html, "중개사무소", "대표자", "등록번호")

    warnings = []

    if image_count <= 0:
        warnings.append("사진 정보가 부족합니다. 사진형 블록 없이 발행해야 합니다.")
    elif image_count == 1:
        warnings.append("사진이 1장입니다. 대표이미지 중심의 간결형 발행이 적합합니다.")

    if broker_desc_len < 20:
        warnings.append("중개사 매물설명이 매우 짧습니다. 임의 장점 생성 없이 사실 중심으로 발행해야 합니다.")

    if not has_legal:
        warnings.append("법정표시사항이 본문에서 확인되지 않습니다.")
    if not has_realtor:
        warnings.append("중개사 정보가 본문에서 충분히 확인되지 않습니다.")

    publish_allowed = True
    block_reasons = []

    if mismatch["critical"]:
        publish_allowed = False
        block_reasons.append(mismatch["reason"])

    if not has_legal:
        publish_allowed = False
        block_reasons.append("legal_disclosure_missing")

    result = {
        "draft_id": draft.get("id"),
        "realtor_id": draft.get("realtor_id"),
        "article_no": draft.get("article_no"),
        "title": draft.get("draft_title"),
        "property_class": property_class,
        "image_count": image_count,
        "broker_description_length": broker_desc_len,
        "layout_mode": layout_mode,
        "publish_allowed": publish_allowed,
        "policy": "publish_always_unless_critical_error",
        "block_reasons": block_reasons,
        "warnings": warnings,
        "critical_mismatch": mismatch,
        "recommended_action": "",
    }

    if publish_allowed:
        result["recommended_action"] = f"발행 가능. 정보량에 맞춰 {layout_mode} 레이아웃 사용."
    else:
        result["recommended_action"] = "발행 전 수정 필요. 정보 부족이 아니라 명백한 오류입니다."

    return result


def print_report(result):
    print("=" * 80)
    print("[STEP111-11 HEE PUBLISH-ALWAYS POLICY]")
    print("draft_id:", result["draft_id"])
    print("realtor_id:", result["realtor_id"])
    print("article_no:", result["article_no"])
    print("title:", result["title"])
    print("property_class:", result["property_class"])
    print("image_count:", result["image_count"])
    print("broker_description_length:", result["broker_description_length"])
    print("layout_mode:", result["layout_mode"])
    print("publish_allowed:", result["publish_allowed"])
    print("-" * 80)
    print("[POLICY]")
    print("정상 수집 매물은 발행 대상입니다.")
    print("정보/사진 부족은 발행 차단이 아니라 레이아웃 단순화 기준입니다.")
    print("-" * 80)
    print("[BLOCK REASONS]")
    print(", ".join(result["block_reasons"]) or "(none)")
    print("-" * 80)
    print("[WARNINGS]")
    if result["warnings"]:
        for w in result["warnings"]:
            print("-", w)
    else:
        print("(none)")
    print("-" * 80)
    print("[RECOMMENDED ACTION]")
    print(result["recommended_action"])
    print("=" * 80)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--draft-id", type=int, default=None)
    parser.add_argument("--realtor-id", type=int, default=None)
    parser.add_argument("--article-no", default="")
    parser.add_argument("--json", action="store_true")
    args = parser.parse_args()

    if not args.draft_id and not (args.realtor_id and args.article_no):
        raise RuntimeError("--draft-id 또는 --realtor-id + --article-no 필요")

    conn = get_conn()
    try:
        draft = fetch_draft(conn, draft_id=args.draft_id, realtor_id=args.realtor_id, article_no=args.article_no)
        if not draft:
            raise RuntimeError("draft not found")

        article = fetch_article(conn, draft.get("realtor_id"), draft.get("article_no"))
        if not article:
            article = {}

        result = evaluate_policy(draft, article)

        if args.json:
            print(json.dumps(result, ensure_ascii=False, indent=2, default=str))
        else:
            print_report(result)

    finally:
        conn.close()


if __name__ == "__main__":
    main()
