# -*- coding: utf-8 -*-
"""
tools/generate_realtor_map_asset.py

중개사 주소를 기반으로 지도 카드 이미지를 생성하고 Cafe24 CDN에 업로드한 뒤
blog_realtors.map_* 컬럼을 업데이트한다.

주의:
- 카카오 Local REST API는 주소 → 좌표 변환에만 사용한다.
- 카카오 지도 타일 이미지를 다운로드/재배포하지 않는다.
- 네이버 블로그 본문에는 Cafe24 CDN에 업로드된 자체 제작 지도 카드 이미지를 <img>로 삽입한다.

사용 예:
    set KAKAO_REST_API_KEY=카카오_REST_API_키
    python tools\generate_realtor_map_asset.py --realtor-id 4

    python tools\generate_realtor_map_asset.py --all
"""

import os
import sys
import argparse
import json
import re
from datetime import datetime
from pathlib import Path

import requests
from PIL import Image, ImageDraw, ImageFont

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

from db import get_conn
from services.cafe24_cdn_service import upload_header_image_to_cafe24


OUTPUT_DIR = BASE_DIR / "storage" / "realtor_maps"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)


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


def table_columns(conn, table_name):
    try:
        with conn.cursor() as cur:
            cur.execute(f"SHOW COLUMNS FROM {table_name}")
            rows = cur.fetchall()
        return set(row["Field"] for row in rows)
    except Exception:
        return set()


def get_font(size=28, bold=False):
    candidates = []

    if bold:
        candidates += [
            "C:/Windows/Fonts/malgunbd.ttf",
            "C:/Windows/Fonts/NanumGothicBold.ttf",
            "/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf",
        ]

    candidates += [
        "C:/Windows/Fonts/malgun.ttf",
        "C:/Windows/Fonts/NanumGothic.ttf",
        "/usr/share/fonts/truetype/nanum/NanumGothic.ttf",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
    ]

    for path in candidates:
        try:
            if os.path.exists(path):
                return ImageFont.truetype(path, size)
        except Exception:
            pass

    return ImageFont.load_default()


def wrap_text(draw, text, font, max_width):
    text = clean_text(text)
    if not text:
        return []

    words = re.split(r"(\s+)", text)
    lines = []
    current = ""

    for word in words:
        test = current + word
        try:
            width = draw.textbbox((0, 0), test, font=font)[2]
        except Exception:
            width = len(test) * 12

        if width <= max_width:
            current = test
            continue

        if current.strip():
            lines.append(current.strip())
        current = word.strip()

    if current.strip():
        lines.append(current.strip())

    # 한글 주소처럼 공백이 적은 경우 글자 단위 보정
    fixed = []
    for line in lines:
        try:
            width = draw.textbbox((0, 0), line, font=font)[2]
        except Exception:
            width = len(line) * 12

        if width <= max_width:
            fixed.append(line)
            continue

        buf = ""
        for ch in line:
            test = buf + ch
            try:
                w = draw.textbbox((0, 0), test, font=font)[2]
            except Exception:
                w = len(test) * 12

            if w > max_width and buf:
                fixed.append(buf)
                buf = ch
            else:
                buf = test

        if buf:
            fixed.append(buf)

    return fixed


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),))
        return cur.fetchone() or {}


def fetch_realtors_for_batch(conn):
    with conn.cursor() as cur:
        cur.execute("""
            SELECT *
            FROM blog_realtors
            WHERE COALESCE(office_address, address, '') <> ''
              AND COALESCE(status, 'active') = 'active'
            ORDER BY id ASC
        """)
        return cur.fetchall()


def kakao_geocode(address, rest_api_key):
    address = clean_text(address)
    if not address:
        raise Exception("주소가 비어 있습니다.")

    if not rest_api_key:
        raise Exception("KAKAO_REST_API_KEY 값이 없습니다.")

    url = "https://dapi.kakao.com/v2/local/search/address.json"
    res = requests.get(
        url,
        headers={"Authorization": f"KakaoAK {rest_api_key}"},
        params={"query": address},
        timeout=20,
    )

    if res.status_code >= 400:
        raise Exception(f"Kakao geocode HTTP {res.status_code}: {res.text[:500]}")

    data = res.json()
    docs = data.get("documents") or []

    if not docs:
        raise Exception(f"주소 좌표 변환 결과가 없습니다: {address}")

    doc = docs[0]
    x = float(doc.get("x"))  # longitude
    y = float(doc.get("y"))  # latitude

    return {
        "lat": y,
        "lng": x,
        "raw": doc,
    }


def draw_pin(draw, x, y, color=(3, 199, 90)):
    # 간단한 지도 핀
    draw.ellipse((x - 22, y - 22, x + 22, y + 22), fill=color)
    draw.polygon([(x, y + 44), (x - 16, y + 12), (x + 16, y + 12)], fill=color)
    draw.ellipse((x - 8, y - 8, x + 8, y + 8), fill=(255, 255, 255))


def create_map_card_image(realtor, lat, lng, output_path):
    """
    실제 지도 타일을 저장하지 않고, 블로그용 자체 지도형 이미지를 생성한다.
    - 큰 지도 배경
    - 도로 느낌의 라인
    - 중앙 중개사 위치 마커
    - 하단 정보 카드
    """
    office_name = clean_text(realtor.get("office_name") or "중개사무소")
    representative_name = clean_text(realtor.get("representative_name") or "")
    office_phone = clean_text(realtor.get("office_phone") or realtor.get("phone") or "")
    mobile_phone = clean_text(realtor.get("mobile_phone") or realtor.get("mobile") or "")
    address = clean_text(realtor.get("office_address") or realtor.get("address") or "")

    W, H = 900, 540
    img = Image.new("RGB", (W, H), (235, 241, 246))
    draw = ImageDraw.Draw(img)

    font_title = get_font(34, bold=True)
    font_sub = get_font(22, bold=True)
    font_body = get_font(20)
    font_small = get_font(16)
    font_label = get_font(18, bold=True)

    # 지도 배경
    for x in range(-40, W + 80, 72):
        draw.line((x, 0, x + 120, H), fill=(222, 231, 238), width=2)

    for y in range(0, H, 70):
        draw.line((0, y, W, y), fill=(226, 234, 240), width=2)

    # 큰 도로
    draw.line((-40, 410, 940, 120), fill=(197, 214, 226), width=34)
    draw.line((80, -40, 760, 570), fill=(205, 220, 230), width=26)
    draw.line((-30, 190, 930, 455), fill=(214, 226, 235), width=22)
    draw.line((240, -40, 430, 570), fill=(220, 230, 237), width=16)

    # 도로 중앙선 느낌
    draw.line((-40, 410, 940, 120), fill=(246, 248, 250), width=3)
    draw.line((80, -40, 760, 570), fill=(246, 248, 250), width=3)

    # 주변 블록
    block_color = (248, 250, 252)
    outline = (212, 222, 230)

    for box in [
        (48, 46, 250, 132),
        (610, 54, 842, 146),
        (82, 260, 280, 350),
        (600, 300, 835, 406),
        (326, 70, 510, 160),
        (340, 362, 526, 470),
    ]:
        draw.rounded_rectangle(box, radius=16, fill=block_color, outline=outline, width=1)

    # 중앙 위치 마커
    pin_x, pin_y = 450, 238
    draw.ellipse((pin_x - 34, pin_y - 34, pin_x + 34, pin_y + 34), fill=(3, 199, 90))
    draw.polygon(
        [
            (pin_x, pin_y + 78),
            (pin_x - 25, pin_y + 18),
            (pin_x + 25, pin_y + 18),
        ],
        fill=(3, 199, 90),
    )
    draw.ellipse((pin_x - 13, pin_y - 13, pin_x + 13, pin_y + 13), fill=(255, 255, 255))

    # 마커 라벨
    label_w, label_h = 360, 50
    label_x, label_y = pin_x - label_w // 2, pin_y - 96
    draw.rounded_rectangle(
        (label_x, label_y, label_x + label_w, label_y + label_h),
        radius=18,
        fill=(17, 24, 39),
    )
    office_label = office_name
    if len(office_label) > 18:
        office_label = office_label[:18] + "..."
    draw.text((label_x + 20, label_y + 12), office_label, fill=(255, 255, 255), font=font_label)

    # 하단 정보 카드
    card_x1, card_y1, card_x2, card_y2 = 48, 348, 852, 506
    draw.rounded_rectangle(
        (card_x1, card_y1, card_x2, card_y2),
        radius=24,
        fill=(255, 255, 255),
        outline=(203, 213, 225),
        width=2,
    )

    draw.rounded_rectangle((78, 374, 210, 412), radius=14, fill=(3, 199, 90))
    draw.text((98, 381), "위치 안내", fill=(255, 255, 255), font=font_small)

    draw.text((230, 376), office_name, fill=(17, 24, 39), font=font_title)

    y = 426

    meta_parts = []
    if representative_name:
        meta_parts.append(f"대표자 {representative_name}")

    phone_line = " / ".join([x for x in [office_phone, mobile_phone] if x])
    if phone_line:
        meta_parts.append(f"상담문의 {phone_line}")

    if meta_parts:
        draw.text((78, y), "   ".join(meta_parts), fill=(55, 65, 81), font=font_body)
        y += 32

    address_lines = wrap_text(draw, address, font_body, 690)
    for line in address_lines[:2]:
        draw.text((78, y), line, fill=(55, 65, 81), font=font_body)
        y += 30

    coord_text = f"좌표 {lat:.6f}, {lng:.6f}"
    draw.text((620, 478), coord_text, fill=(107, 114, 128), font=font_small)

    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    img.save(output_path, "PNG", optimize=True)

    return str(output_path)


def update_realtor_map_result(conn, realtor_id, lat=None, lng=None, image_path="", image_url="", error_message=""):
    columns = table_columns(conn, "blog_realtors")

    data = {}

    if "map_lat" in columns:
        data["map_lat"] = lat

    if "map_lng" in columns:
        data["map_lng"] = lng

    if "map_image_path" in columns:
        data["map_image_path"] = image_path

    if "map_image_url" in columns:
        data["map_image_url"] = image_url

    if "map_generated_at" in columns:
        data["map_generated_at"] = datetime.now() if image_url else None

    if "map_error_message" in columns:
        data["map_error_message"] = error_message[:2000] if error_message else None

    if "updated_at" in columns:
        data["updated_at"] = datetime.now()

    if not data:
        print("[MAP UPDATE SKIP] blog_realtors map columns not found")
        return

    sets = ", ".join([f"{k} = %s" for k in data.keys()])
    values = list(data.values())
    values.append(int(realtor_id))

    with conn.cursor() as cur:
        cur.execute(f"UPDATE blog_realtors SET {sets} WHERE id = %s", values)

    conn.commit()


def generate_for_realtor(conn, realtor, rest_api_key):
    realtor_id = int(realtor.get("id") or 0)
    office_name = clean_text(realtor.get("office_name") or "")
    address = clean_text(realtor.get("office_address") or realtor.get("address") or "")

    if not realtor_id:
        raise Exception("realtor_id가 없습니다.")

    if not address:
        raise Exception(f"주소가 없습니다. realtor_id={realtor_id}")

    print(f"[MAP START] realtor_id={realtor_id}, office={office_name}")

    geo = kakao_geocode(address, rest_api_key)
    lat = geo["lat"]
    lng = geo["lng"]

    filename = f"realtor_map_{realtor_id}_{datetime.now().strftime('%Y%m%d%H%M%S')}.png"
    local_path = OUTPUT_DIR / filename

    image_path = create_map_card_image(
        realtor=realtor,
        lat=lat,
        lng=lng,
        output_path=local_path,
    )

    # Cafe24 업로드 함수 재사용
    # article_no 자리에 realtor_{id}를 넣어 realtor map 자산으로 구분한다.
    cdn = upload_header_image_to_cafe24(
        conn=conn,
        article_no=f"realtor_{realtor_id}",
        local_path=image_path,
        filename=filename,
    )

    image_url = clean_text((cdn or {}).get("cdn_url") or "")

    if not image_url:
        raise Exception("Cafe24 CDN 업로드 결과 URL이 없습니다.")

    update_realtor_map_result(
        conn=conn,
        realtor_id=realtor_id,
        lat=lat,
        lng=lng,
        image_path=image_path,
        image_url=image_url,
        error_message="",
    )

    print(f"[MAP DONE] realtor_id={realtor_id}, url={image_url}")

    return {
        "realtor_id": realtor_id,
        "lat": lat,
        "lng": lng,
        "image_path": image_path,
        "image_url": image_url,
    }


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--realtor-id", type=int, default=0)
    parser.add_argument("--all", action="store_true")
    parser.add_argument("--kakao-rest-api-key", type=str, default="")
    args = parser.parse_args()

    rest_api_key = args.kakao_rest_api_key or os.environ.get("KAKAO_REST_API_KEY", "")

    conn = get_conn()

    success = 0
    failed = 0

    try:
        if args.all:
            targets = fetch_realtors_for_batch(conn)
        else:
            if args.realtor_id <= 0:
                raise Exception("--realtor-id 또는 --all 옵션이 필요합니다.")
            targets = [fetch_realtor(conn, args.realtor_id)]

        print("=" * 80)
        print(f"[MAP TARGET COUNT] {len(targets)}")
        print("=" * 80)

        for realtor in targets:
            if not realtor:
                continue

            realtor_id = int(realtor.get("id") or 0)

            try:
                generate_for_realtor(conn, realtor, rest_api_key)
                success += 1

            except Exception as e:
                failed += 1
                print(f"[MAP ERROR] realtor_id={realtor_id} / {e}")

                try:
                    update_realtor_map_result(
                        conn=conn,
                        realtor_id=realtor_id,
                        error_message=str(e),
                    )
                except Exception:
                    pass

        print("=" * 80)
        print(f"[MAP FINISH] success={success}, failed={failed}")
        print("=" * 80)

    finally:
        try:
            conn.close()
        except Exception:
            pass


if __name__ == "__main__":
    main()
