# -*- coding: utf-8 -*-
"""
STEP110-02 Draft Quality Checker v2.1

운영 품질 유지/향상 기준:
- 단순 점수보다 CRITICAL FAIL을 우선한다.
- '확인 필요' 오탐 방지:
  관리비 문구의 '별도 확인 필요'는 중개사 정보 누락으로 보지 않는다.
- 주소 중복은 warning으로 표시한다.

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

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

  article_no 기준 최신 초안:
  python tools\check_draft_quality_v2.py --realtor-id 1 --article-no 2633019770

  JSON:
  python tools\check_draft_quality_v2.py --draft-id 209 --json
"""

import argparse
import re
import sys
import json
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 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(html):
    return len(re.findall(r"<img\b", html or "", flags=re.I))


def has_any(html, patterns):
    src = html or ""
    for p in patterns:
        if re.search(p, src, flags=re.I | re.S):
            return True
    return False


def extract_broker_description_text(html):
    html = html or ""

    patterns = [
        r"중개사\s*매물설명.*?<div[^>]*font-size:16px[^>]*>(.*?)</div>",
        r"중개사\s*매물설명(.*?)(?:상담\s*및\s*중개사\s*안내|중개대상물\s*표시|realestate-realtor-map-image)",
    ]

    for pattern in patterns:
        m = re.search(pattern, html, flags=re.I | re.S)
        if m:
            return clean_text(m.group(1))

    return ""


def extract_section_between(text, start_label, end_labels=None, max_len=2500):
    text = text or ""
    idx = text.find(start_label)
    if idx < 0:
        return ""

    part = text[idx:idx + max_len]

    if end_labels:
        cut_positions = []
        for label in end_labels:
            pos = part.find(label, len(start_label))
            if pos > 0:
                cut_positions.append(pos)

        if cut_positions:
            part = part[:min(cut_positions)]

    return part


def evaluate_draft(draft):
    html = draft.get("clipboard_html") or draft.get("draft_html") or ""
    text = clean_text(html)
    broker_desc = extract_broker_description_text(html)
    image_count = count_images(html)

    checks = []
    criticals = []

    def add(name, ok, score, message, critical=False):
        item = {
            "name": name,
            "ok": bool(ok),
            "score": int(score if ok else 0),
            "max_score": int(score),
            "message": message,
            "critical": bool(critical),
        }
        checks.append(item)

        if critical and not ok:
            criticals.append({
                "name": name,
                "message": message,
            })

    has_rep = has_any(html, [r"realestate-representative-image", r"header_.*?\.png"])
    has_map = has_any(html, [r"realestate-realtor-map-image", r"realtor_naver_map_"])
    has_legal = has_any(html, [r"realestate-legal-disclosure-table", r"중개대상물\s*표시"])
    has_floorplan = has_any(html, [r"평면도", r"floorplan", r"photoinfra"])
    has_seo = has_any(html, [r"realestate-seo-footer", r"관련\s*키워드"])
    has_address = "소재지" in text or "주소" in text

    # 오탐 방지:
    # 전체 본문의 '관리비 ... 별도 확인 필요'는 허용.
    # 중개사무소 명시사항 섹션 안의 '확인 필요'만 중개사 정보 누락으로 판단.
    realtor_legal_section = extract_section_between(
        text,
        "중개사무소 및 개업공인중개사 명시사항",
        end_labels=["중개대상물 명시사항", "본 포스팅의 매물 정보"],
        max_len=1800,
    )

    realtor_contact_section = extract_section_between(
        text,
        "상담 및 중개사 안내",
        end_labels=["중개대상물 표시", "중개사무소 및 개업공인중개사 명시사항"],
        max_len=1800,
    )

    realtor_sections = (realtor_legal_section + " " + realtor_contact_section).strip()

    has_empty_realtor = "확인 필요" in realtor_sections

    add("대표이미지", has_rep, 15, "대표이미지 블록 또는 header 이미지가 필요합니다.", critical=True)
    add("중개사 지도", has_map, 15, "중개사 지도 이미지가 포함되어야 합니다.", critical=True)
    add("중개사 매물설명", bool(broker_desc), 15, f"중개사 매물설명 길이={len(broker_desc)}", critical=True)
    add("매물설명 충분성", len(broker_desc) >= 40, 10, f"현재={len(broker_desc)}자. 최소 40자 이상 권장.", critical=False)
    add("소재지", has_address, 10, "소재지 또는 주소가 명시되어야 합니다.", critical=True)
    add("법정 표시사항", has_legal, 10, "중개대상물 표시·광고 명시사항이 필요합니다.", critical=True)
    add("평면도", has_floorplan, 8, "평면도 또는 구조 이미지가 있으면 좋습니다.", critical=False)
    add("이미지 개수", image_count >= 4, 7, f"이미지 수={image_count}. 최소 4장 이상 권장.", critical=False)
    add("중개사 정보 완성도", not has_empty_realtor, 5, "중개사 정보 섹션에 '확인 필요'가 없어야 합니다.", critical=True)
    add("SEO footer", has_seo, 5, "SEO footer 또는 관련 키워드가 포함되면 좋습니다.", critical=False)

    warnings = []

    if image_count <= 2:
        warnings.append("이미지 수가 매우 적습니다. 실내/외관/입지 이미지 확보 필요.")

    if len(broker_desc) < 80:
        warnings.append("중개사 매물설명이 짧습니다. 원문이 짧은 매물인지 확인 필요.")

    if "세종시 세종시" in text:
        warnings.append("주소에 '세종시 세종시' 중복 표기가 있습니다. 주소 정규화 필요.")

    total = sum(x["score"] for x in checks)
    max_total = sum(x["max_score"] for x in checks)

    if criticals:
        grade = "CRITICAL_FAIL"
        publish_allowed = False
    elif total >= 90:
        grade = "A"
        publish_allowed = True
    elif total >= 80:
        grade = "B"
        publish_allowed = True
    elif total >= 70:
        grade = "C"
        publish_allowed = False
    else:
        grade = "D"
        publish_allowed = False

    return {
        "draft_id": draft.get("id"),
        "realtor_id": draft.get("realtor_id"),
        "article_no": draft.get("article_no"),
        "draft_title": draft.get("draft_title"),
        "image_count": image_count,
        "broker_description_length": len(broker_desc),
        "score": total,
        "max_score": max_total,
        "grade": grade,
        "publish_allowed": publish_allowed,
        "critical_count": len(criticals),
        "criticals": criticals,
        "warnings": warnings,
        "checks": checks,
    }


def print_report(report):
    print("=" * 80)
    print("[STEP110 DRAFT QUALITY REPORT V2.1]")
    print("draft_id:", report.get("draft_id"))
    print("realtor_id:", report.get("realtor_id"))
    print("article_no:", report.get("article_no"))
    print("title:", report.get("draft_title"))
    print("score:", f"{report.get('score')}/{report.get('max_score')}")
    print("grade:", report.get("grade"))
    print("publish_allowed:", report.get("publish_allowed"))
    print("image_count:", report.get("image_count"))
    print("broker_description_length:", report.get("broker_description_length"))
    print("-" * 80)

    for item in report["checks"]:
        mark = "OK" if item["ok"] else ("CRITICAL" if item["critical"] else "FAIL")
        print(f"[{mark}] {item['name']} ({item['score']}/{item['max_score']}) - {item['message']}")

    if report["criticals"]:
        print("-" * 80)
        print("[CRITICAL FAIL]")
        for item in report["criticals"]:
            print(f"- {item['name']}: {item['message']}")

    if report["warnings"]:
        print("-" * 80)
        print("[WARNINGS]")
        for item in report["warnings"]:
            print(f"- {item}")

    print("=" * 80)

    if report["publish_allowed"]:
        print("[QUALITY PASS] 운영 발행 가능 기준 통과")
    else:
        print("[QUALITY BLOCK] 운영 발행 전 보완 필요")


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")

        report = evaluate_draft(draft)

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

    finally:
        conn.close()


if __name__ == "__main__":
    main()
