# -*- coding: utf-8 -*-
"""
STEP111-01 HBE Story Planner v1

목적:
- 운영 초안 생성 코드는 건드리지 않는다.
- 기존 draft/source_json/html을 읽어서 "사람이 쓴 블로그처럼 보이기 위한 이야기 방향"을 추천한다.

사용:
  cd /d D:\honghee\blog_api
  python tools\hbe_story_planner.py --draft-id 209
  python tools\hbe_story_planner.py --realtor-id 1 --article-no 2633019770
  python tools\hbe_story_planner.py --draft-id 209 --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 count_images_by_hint(html):
    html = html or ""
    return {
        "total": len(re.findall(r"<img\b", html, flags=re.I)),
        "floorplan": len(re.findall(r"floorplan|평면도|photoinfra", html, flags=re.I)),
        "realtor_map": len(re.findall(r"realtor_naver_map_|realestate-realtor-map-image", html, flags=re.I)),
        "location": len(re.findall(r"입지|주변환경|land_naver", html, flags=re.I)),
        "header": len(re.findall(r"realestate-representative-image|header_", html, flags=re.I)),
    }


def extract_source_json(draft):
    return safe_json_loads(draft.get("source_json")) or {"detail": {}, "images": [], "schools": [], "prices": []}


def get_nested(source, *keys):
    cur = source
    for key in keys:
        if not isinstance(cur, dict):
            return None
        cur = cur.get(key)
    return cur


def analyze_signals(draft):
    html = draft.get("clipboard_html") or draft.get("draft_html") or ""
    text = clean_text(html)
    source = extract_source_json(draft)
    detail = source.get("detail") or {}
    schools = source.get("schools") or []
    prices = source.get("prices") or []

    raw_json = safe_json_loads(detail.get("raw_json"))
    article_detail = raw_json.get("articleDetail") or {}
    article_realtor = raw_json.get("articleRealtor") or {}

    broker_desc = clean_text(
        detail.get("article_feature_desc")
        or article_detail.get("detailDescription")
        or article_detail.get("articleFeatureDescription")
        or ""
    )

    combined = " ".join([
        clean_text(draft.get("draft_title")),
        clean_text(detail.get("article_name")),
        clean_text(detail.get("article_feature_desc")),
        broker_desc,
        text[:5000],
    ])

    image_counts = count_images_by_hint(html)

    keyword_groups = {
        "school": ["초", "중", "고", "학교", "학군", "통학", "배정학교"],
        "family": ["대단지", "세대", "커뮤니티", "도서관", "게스트하우스", "스포츠센터", "놀이터"],
        "transport": ["역", "지하철", "BRT", "brt", "정류장", "교통", "버스", "IC", "대로"],
        "newly": ["신축", "4년이내", "준신축", "신축급", "깨끗"],
        "option": ["풀옵션", "옵션", "냉장고", "세탁기", "에어컨", "인덕션", "붙박이"],
        "view_light": ["채광", "조망", "남향", "남동향", "남서향", "뷰"],
        "price": ["급매", "가격", "저렴", "시세", "투자", "수익", "전세가"],
        "movein": ["입주", "즉시", "협의", "빠른입주"],
    }

    scores = {}
    lower_combined = combined.lower()
    for group, words in keyword_groups.items():
        hits = [word for word in words if word.lower() in lower_combined]
        scores[group] = {"score": len(hits), "hits": hits}

    scores["location_photo"] = {"score": 3 if image_counts["location"] >= 4 else 0, "hits": ["입지이미지多"] if image_counts["location"] >= 4 else []}
    scores["structure"] = {"score": 2 if image_counts["floorplan"] >= 1 else 0, "hits": ["평면도"] if image_counts["floorplan"] >= 1 else []}

    if len(schools) > 0:
        scores["school"]["score"] += 2
        scores["school"]["hits"].append("schools_table")

    if len(prices) > 0:
        scores["price"]["score"] += 1
        scores["price"]["hits"].append("prices_table")

    property_type = clean_text(
        detail.get("real_estate_type")
        or article_detail.get("realestateTypeName")
        or get_nested(raw_json, "articleAddition", "realEstateTypeName")
        or ""
    )

    transaction_type = clean_text(
        detail.get("trade_type")
        or article_detail.get("tradeTypeName")
        or get_nested(raw_json, "articleAddition", "tradeTypeName")
        or ""
    )

    return {
        "draft_id": draft.get("id"),
        "realtor_id": draft.get("realtor_id"),
        "article_no": draft.get("article_no"),
        "title": draft.get("draft_title"),
        "property_type": property_type,
        "transaction_type": transaction_type,
        "broker_desc_len": len(broker_desc),
        "broker_desc": broker_desc[:500],
        "image_counts": image_counts,
        "school_count": len(schools),
        "price_count": len(prices),
        "realtor_name": clean_text(article_realtor.get("realtorName") or ""),
        "scores": scores,
    }


def pick_story_profile(signals):
    ranked = sorted(signals["scores"].items(), key=lambda kv: kv[1].get("score", 0), reverse=True)
    top_key = ranked[0][0] if ranked and ranked[0][1].get("score", 0) > 0 else "general"

    profile_map = {
        "school": ("family_school_story", "학군·실거주 강조형", "학교, 단지 규모, 생활 편의성을 중심으로 실거주 관점에서 풀어갑니다."),
        "family": ("large_complex_living_story", "대단지 생활권 강조형", "대단지, 커뮤니티, 생활 편의시설을 중심으로 자연스럽게 설명합니다."),
        "transport": ("transport_access_story", "교통·접근성 강조형", "정류장, 도로 접근성, 생활 동선을 중심으로 글을 전개합니다."),
        "newly": ("condition_clean_story", "컨디션·신축감 강조형", "내부 상태, 연식, 관리 상태를 중심으로 신뢰감을 줍니다."),
        "option": ("option_practical_story", "옵션·실용성 강조형", "옵션 구성과 실제 사용 편의성을 중심으로 설명합니다."),
        "view_light": ("light_view_story", "채광·방향 강조형", "방향, 채광, 조망 등 현장 체감 포인트를 중심으로 씁니다."),
        "price": ("price_condition_story", "가격·조건 강조형", "가격 조건과 비교 포인트를 중심으로 신중하게 설명합니다."),
        "location_photo": ("photo_location_story", "입지 사진 중심형", "입지 사진과 주변 환경 이미지를 중심으로 현장감을 줍니다."),
        "structure": ("structure_floorplan_story", "구조·평면도 중심형", "평면도, 면적, 공간 활용을 중심으로 설명합니다."),
        "general": ("balanced_human_story", "균형형", "핵심 정보, 사진, 중개사 설명을 균형 있게 배치합니다."),
    }

    profile, name, angle = profile_map.get(top_key, profile_map["general"])

    return {
        "profile": profile,
        "name": name,
        "angle": angle,
        "top_signal": top_key,
        "ranked_signals": [
            {"key": k, "score": v.get("score", 0), "hits": v.get("hits", [])}
            for k, v in ranked[:5]
        ],
    }


def recommend_layout(signals, story):
    image_counts = signals["image_counts"]
    broker_len = signals["broker_desc_len"]
    profile = story["profile"]

    if profile in ["family_school_story", "large_complex_living_story"]:
        layout = "story_photo_living"
        blocks = ["representative_image", "human_intro_from_broker", "location_or_complex_photo", "broker_description", "key_info_table", "floorplan", "photo_story", "community_or_school_note", "middle_banner", "realtor_map", "realtor_info", "legal_disclosure", "seo_footer"]
    elif profile in ["structure_floorplan_story", "option_practical_story"]:
        layout = "structure_first_review"
        blocks = ["representative_image", "human_intro_from_broker", "key_info_table", "floorplan", "space_commentary", "photo_story", "broker_description", "middle_banner", "realtor_map", "realtor_info", "legal_disclosure", "seo_footer"]
    elif profile in ["photo_location_story", "transport_access_story"]:
        layout = "location_photo_journal"
        blocks = ["representative_image", "human_intro_from_broker", "location_photo_story", "key_info_table", "transport_living_note", "floorplan", "broker_description", "middle_banner", "realtor_map", "realtor_info", "legal_disclosure", "seo_footer"]
    elif profile == "price_condition_story":
        layout = "condition_checklist"
        blocks = ["representative_image", "key_info_table", "price_condition_note", "photo_story", "floorplan", "broker_description", "checklist", "middle_banner", "realtor_map", "realtor_info", "legal_disclosure", "seo_footer"]
    else:
        layout = "balanced_human_review"
        blocks = ["representative_image", "human_intro_from_broker", "key_info_table", "photo_story", "floorplan", "broker_description", "location_note", "middle_banner", "realtor_map", "realtor_info", "legal_disclosure", "seo_footer"]

    if image_counts["total"] < 6:
        blocks = [b for b in blocks if b not in ["photo_story", "location_photo_story"]]
        layout += "_low_photo"

    if broker_len >= 150 and "broker_description" in blocks:
        blocks = [b for b in blocks if b != "broker_description"]
        blocks.insert(2, "broker_description")
        layout += "_broker_first"

    return {
        "layout_id": layout,
        "blocks": blocks,
        "must_have": ["representative_image", "legal_disclosure", "realtor_info", "realtor_map", "seo_footer"],
        "variation_notes": [
            "같은 중개사의 최근 3회 layout_id는 제외하는 방식으로 반복감을 줄입니다.",
            "블록 순서는 story_profile과 이미지 수, 중개사 설명 길이에 따라 조정합니다.",
            "법정표시/지도/중개사정보/SEO는 위치만 바뀔 수 있고 누락되면 안 됩니다.",
        ],
    }


def print_report(signals, story, layout):
    print("=" * 80)
    print("[STEP111-01 HBE STORY PLANNER]")
    print("draft_id:", signals.get("draft_id"))
    print("realtor_id:", signals.get("realtor_id"))
    print("article_no:", signals.get("article_no"))
    print("title:", signals.get("title"))
    print("property_type:", signals.get("property_type"))
    print("transaction_type:", signals.get("transaction_type"))
    print("broker_desc_len:", signals.get("broker_desc_len"))
    print("image_counts:", json.dumps(signals.get("image_counts"), ensure_ascii=False))
    print("-" * 80)
    print("[STORY PROFILE]")
    print("profile:", story.get("profile"))
    print("name:", story.get("name"))
    print("angle:", story.get("angle"))
    print("top_signal:", story.get("top_signal"))
    print("ranked_signals:")
    for item in story.get("ranked_signals") or []:
        print(" -", item["key"], "score=", item["score"], "hits=", ",".join(item["hits"]))
    print("-" * 80)
    print("[LAYOUT RECOMMENDATION]")
    print("layout_id:", layout.get("layout_id"))
    print("blocks:")
    for i, block in enumerate(layout.get("blocks") or [], start=1):
        print(f" {i:02d}. {block}")
    print("-" * 80)
    print("[BROKER DESC SAMPLE]")
    print(signals.get("broker_desc") or "(empty)")
    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")

        signals = analyze_signals(draft)
        story = pick_story_profile(signals)
        layout = recommend_layout(signals, story)
        result = {"signals": signals, "story": story, "layout": layout}

        if args.json:
            print(json.dumps(result, ensure_ascii=False, indent=2, default=str))
        else:
            print_report(signals, story, layout)
    finally:
        conn.close()


if __name__ == "__main__":
    main()
