# -*- coding: utf-8 -*-
"""
STEP111-14 HEE Policy Service v1

역할:
- 네이버부동산 원천 데이터 기반 매물유형 판별
- 정보량 기반 layout_mode 결정
- layout_mode -> 실제 blog_layout_blocks.py layout_type 후보/추천값 결정
- 원칙: 정상 수집 매물은 발행 대상. 정보 부족은 발행 차단이 아니라 compact/simple 처리.

주의:
- 이 파일은 운영 DB를 직접 수정하지 않는다.
- generate_blog_drafts.py에 흡수하기 전, tools/test_hee_policy_service.py로 검증한다.
"""

import json
import random
import re
from html import unescape


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 raw_json_get(detail, path, default=""):
    raw = safe_json_loads((detail or {}).get("raw_json"))
    cur = raw

    for key in path:
        if not isinstance(cur, dict):
            return default
        cur = cur.get(key)

    return cur if cur is not None else default


def resolve_property_class(detail):
    """
    매물유형 단일 판별기.

    우선순위:
    1. DB real_estate_type / real_estate_type_name
    2. raw_json articleDetail / articleAddition 원천 유형명
    3. raw_json 유형 코드
    4. fallback unknown

    절대 하지 않는 것:
    - article_name/title에서 '전', '대', '답' 같은 한 글자 부분검색
    - 주소의 '산', '대전', '전세'를 토지로 추론
    """
    detail = detail or {}

    type_names = [
        detail.get("real_estate_type"),
        detail.get("real_estate_type_name"),
        raw_json_get(detail, ["articleDetail", "realestateTypeName"]),
        raw_json_get(detail, ["articleAddition", "articleRealEstateTypeName"]),
        raw_json_get(detail, ["articleAddition", "realEstateTypeName"]),
        raw_json_get(detail, ["articleDetail", "buildingTypeName"]),
    ]

    type_codes = [
        raw_json_get(detail, ["articleDetail", "articleTypeCode"]),
        raw_json_get(detail, ["articleDetail", "realestateTypeCode"]),
        raw_json_get(detail, ["articleDetail", "tradeBuildingTypeCode"]),
        raw_json_get(detail, ["articleAddition", "articleRealEstateTypeCode"]),
        raw_json_get(detail, ["articleAddition", "realEstateTypeCode"]),
    ]

    exact_names = [clean_text(x) for x in type_names if clean_text(x)]
    exact_codes = [clean_text(x).upper() for x in type_codes if clean_text(x)]

    # 건축물/주거형 우선 확정
    if any(x in ["아파트"] for x in exact_names) or any(x in ["A01", "APT"] for x in exact_codes):
        return "apartment"

    if any(x in ["오피스텔"] for x in exact_names) or any(x in ["OPST", "OP"] for x in exact_codes):
        return "officetel"

    if any(x in ["빌라", "연립", "다세대", "주택"] for x in exact_names):
        return "villa_house"

    if any(x in ["상가", "상가점포", "근린상가"] for x in exact_names) or any(x in ["SG", "SMS"] for x in exact_codes):
        return "store"

    if any(x in ["사무실", "업무시설"] for x in exact_names):
        return "office"

    if any(x in ["공장", "창고", "공장/창고"] for x in exact_names) or any(x in ["GJCG", "GM", "CW"] for x in exact_codes):
        return "factory_warehouse"

    if any(x in ["분양권", "입주권"] for x in exact_names):
        return "presale_right"

    # 토지는 정확히 유형명/코드가 토지일 때만
    land_names = {"토지", "대", "전", "답", "임야", "대지", "잡종지", "공장용지", "창고용지", "도로", "구거", "토지/임야"}
    land_codes = {"TJ", "LAND", "TOJI", "E03"}

    if any(x in land_names for x in exact_names) or any(x in land_codes for x in exact_codes):
        return "land"

    return "unknown"


def property_class_to_display_name(property_class):
    return {
        "apartment": "아파트",
        "officetel": "오피스텔",
        "villa_house": "빌라/주택",
        "store": "상가",
        "office": "사무실",
        "factory_warehouse": "공장/창고",
        "land": "토지",
        "presale_right": "분양권/입주권",
        "unknown": "기타",
    }.get(property_class or "unknown", "기타")


def broker_description_length(detail, ai_sections=None):
    raw = safe_json_loads((detail or {}).get("raw_json"))
    article_detail = raw.get("articleDetail") or {}

    desc = clean_text(
        (detail or {}).get("article_feature_desc")
        or (detail or {}).get("article_desc")
        or (detail or {}).get("article_description")
        or article_detail.get("detailDescription")
        or article_detail.get("articleFeatureDescription")
    )

    if not desc and ai_sections:
        desc = clean_text((ai_sections or {}).get("intro") or "")

    return len(desc)


def analyze_source_richness(detail, images=None, ai_sections=None, html=""):
    property_class = resolve_property_class(detail)

    try:
        image_count = len(images or [])
    except Exception:
        image_count = 0

    desc_len = broker_description_length(detail, ai_sections=ai_sections)

    text = clean_text(html)
    has_floorplan = "평면도" in text or "floorplan" in (html or "").lower()
    has_school = any(k in text for k in ["학군", "학교", "초등학교", "중학교", "고등학교"])

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

    return {
        "property_class": property_class,
        "property_name": property_class_to_display_name(property_class),
        "image_count": image_count,
        "broker_description_length": desc_len,
        "has_floorplan": has_floorplan,
        "has_school": has_school,
        "richness": richness,
    }


def choose_layout_mode(analysis):
    property_class = analysis.get("property_class") or "unknown"
    richness = analysis.get("richness") or "simple"

    if property_class == "land":
        return f"land_{richness}"

    if property_class == "factory_warehouse":
        return f"factory_{richness}"

    if property_class in ["store", "office"]:
        return f"business_{richness}"

    if property_class in ["apartment", "officetel", "villa_house", "presale_right"]:
        return f"living_{richness}"

    return f"generic_{richness}"


LAYOUT_CANDIDATES = {
    "living_rich": ["storytelling", "magazine_style", "local_life", "family_recommend", "premium_report", "gallery_story"],
    "living_balanced": ["story_first", "location_focus", "recommendation_focus", "storytelling", "local_life", "photo_grid"],
    "living_simple": ["summary_first", "minimal_modern", "short_review_style", "checklist_style"],
    "living_compact": ["summary_first", "minimal_modern", "checklist_style"],
    "living_minimal": ["summary_first", "minimal_modern"],

    "business_rich": ["office_focus", "location_focus", "broker_expert", "premium_report"],
    "business_balanced": ["office_focus", "location_focus", "summary_first", "checklist_style"],
    "business_simple": ["office_focus", "summary_first", "minimal_modern"],
    "business_compact": ["summary_first", "minimal_modern"],
    "business_minimal": ["summary_first", "minimal_modern"],

    "factory_rich": ["office_focus", "broker_expert", "checklist_style", "summary_first"],
    "factory_balanced": ["office_focus", "summary_first", "checklist_style"],
    "factory_simple": ["summary_first", "checklist_style", "minimal_modern"],
    "factory_compact": ["summary_first", "minimal_modern"],
    "factory_minimal": ["summary_first", "minimal_modern"],

    "land_rich": ["land_focus"],
    "land_balanced": ["land_focus"],
    "land_simple": ["land_focus"],
    "land_compact": ["land_focus"],
    "land_minimal": ["land_focus"],

    "generic_rich": ["story_first", "summary_first", "checklist_style"],
    "generic_balanced": ["summary_first", "checklist_style", "minimal_modern"],
    "generic_simple": ["summary_first", "minimal_modern"],
    "generic_compact": ["summary_first", "minimal_modern"],
    "generic_minimal": ["summary_first", "minimal_modern"],
}


def select_layout_type(layout_mode, realtor_id=None, article_no=None):
    candidates = LAYOUT_CANDIDATES.get(layout_mode) or LAYOUT_CANDIDATES["generic_simple"]
    seed = f"{realtor_id or ''}:{article_no or ''}:{layout_mode}"
    rnd = random.Random(seed)

    return {
        "layout_mode": layout_mode,
        "selected_layout_type": rnd.choice(candidates),
        "candidates": candidates,
        "selection_rule": "deterministic_random_by_realtor_article_layout_mode",
    }


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 resolve_publish_policy(detail, images=None, ai_sections=None, html=""):
    analysis = analyze_source_richness(detail, images=images, ai_sections=ai_sections, html=html)
    layout_mode = choose_layout_mode(analysis)
    layout = select_layout_type(
        layout_mode,
        realtor_id=(detail or {}).get("realtor_id"),
        article_no=(detail or {}).get("article_no"),
    )
    mismatch = detect_critical_mismatch(analysis["property_class"], html)

    publish_allowed = True
    block_reasons = []
    warnings = []

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

    if analysis["image_count"] <= 0:
        warnings.append("사진 정보 부족: 사진형 블록을 줄이고 기본정보 중심으로 발행")
    elif analysis["image_count"] == 1:
        warnings.append("사진 1장: 대표이미지 중심의 간결형 발행")

    if analysis["broker_description_length"] < 20:
        warnings.append("중개사 매물설명 부족: 임의 장점 생성 없이 사실 중심 발행")

    return {
        "policy": "publish_always_unless_critical_error",
        "publish_allowed": publish_allowed,
        "block_reasons": block_reasons,
        "warnings": warnings,
        "analysis": analysis,
        "layout": layout,
    }


def log_policy_recommendation(detail, images=None, ai_sections=None, html="", current_layout_type=None, apply=False):
    result = resolve_publish_policy(detail, images=images, ai_sections=ai_sections, html=html)
    analysis = result["analysis"]
    layout = result["layout"]

    print(
        "[HEE POLICY] "
        f"property_class={analysis.get('property_class')}, "
        f"richness={analysis.get('richness')}, "
        f"image_count={analysis.get('image_count')}, "
        f"broker_desc_len={analysis.get('broker_description_length')}, "
        f"layout_mode={layout.get('layout_mode')}, "
        f"recommended={layout.get('selected_layout_type')}, "
        f"current={current_layout_type}, "
        f"publish_allowed={result.get('publish_allowed')}, "
        f"apply={apply}"
    )

    return result
