# -*- coding: utf-8 -*-
"""
言問名店DB ターゲット・セグメンテーション・エンジン
入力: data/meiten-master.json (202店)
出力: mvp/source/target-patch.json (各店の targetPrimary/targetMix/inboundReady/tags/reason)

判定は「業種(category) x エリア(town) x 評価(rating) x 公式サイト(officialSite) x hotelEastFit」から
定量的・再現可能なルールで導く。ルールは 13_target-segmentation.md に文書化。
GR19: 捏造禁止。確信薄は targetPrimary='要確認' + needsReview=True。
"""
import json, re, os

BASE = r'C:\Users\daito\company\projects\kototoi-sanpo-editorial\meiten-business'
SRC = os.path.join(BASE, 'data', 'meiten-master.json')
OUT = os.path.join(BASE, 'mvp', 'source', 'target-patch.json')

d = json.load(open(SRC, encoding='utf-8'))
stores = d['stores']

# ---- エリア辞書（町名 -> 立地性格）----
# 観光地コア: 訪日・国内観光が高密度に歩く区域
TOURIST_CORE = {'浅草', '浅草（雷門）', '雷門', '花川戸', '西浅草', '西浅草（合羽橋）',
                '上野', '上野公園', '上野桜木', '谷中', '蔵前', '駒形', '向島', '押上'}
# 観光近接: 観光客が一定割合流れるが地元色も濃い
TOURIST_NEAR = {'浅草橋', '柳橋', '元浅草', '池之端', '湯島', '根津', '橋場'}
# 地元コア: 近隣住民の日常利用が主、観光客は少ない
LOCAL_CORE = {'根岸', '根岸（推定）', '下谷', '入谷', '下谷/入谷', '東上野', '三筋', '寿',
              '松が谷', '竜泉', '小島', '日本堤', '東日暮里'}

# ---- 業種辞書（プレフィックスで判定）----
# B2B / 法人: 来街者の現地接点が薄く、取引先・地域企業が顧客
B2B_PREFIX = ('不動産', '金融', '保険', '商社', '卸', '印刷', '士業', '法務', '機械', 'IT',
              '流通', '包装', 'ゴム', 'プラスチック', 'エネルギー', '産業', '広告', '事務',
              '引越', '物流', '建築', '塗装', '電気', '家電', '消費財', '宗教法人', '医薬品',
              '宝飾製造', '問屋', '繊維', '玩具卸', '遊技機', '商業', '公共', '就労', 'DX',
              '法律', '機械要素', '油空圧')
# 視覚/体験で言語を超えて楽しめる業種 = インバウンド適性が構造的に高い
INBOUND_VISUAL_KEYS = ('うなぎ', '寿司', 'すし', 'とんかつ', '天ぷら', '和菓子', '甘味', '甘味処',
                       '焼肉', '焼鳥', '焼き鳥', 'やきとん', 'ラーメン', 'もんじゃ', 'お好み',
                       '抹茶', 'カフェ', 'クラフトビール', 'バー', 'ビストロ', 'フレンチ',
                       'イタリアン', '料亭', '豆腐', 'おにぎり', '炭火', '洋食')
# 物販で外国人土産需要のある業種
INBOUND_RETAIL_KEYS = ('刃物', '玩具', '宝飾', '宝石', '靴', 'バッグ', '鞄', 'ベルト', '文具',
                       '雑貨', '家庭用品', '小売', '食品', '百貨店', '古書', '書籍', '医薬品', '消費財')
# 観光インフラ / 寺社 / 宿泊 = インバウンド直撃
INBOUND_INFRA_KEYS = ('宿泊', 'ホテル', '寺社', '観光', '観光案内', '観光複合', 'サウナ', '入浴')

GROUP_KEYS = ('グループ内',)  # 山陽グループ内
PET_KEYS = ('ペット', '動物病院', '犬カフェ', '猫カフェ')


def cat_has(cat, keys):
    return any(k in cat for k in keys)


def classify(st):
    cat = st.get('category', '') or ''
    town = (st.get('area') or {}).get('town', '') or ''
    rating = st.get('rating')
    site = st.get('officialSite') or ''
    heFit = str(st.get('hotelEastFit', ''))
    label = str(st.get('label', ''))
    reasons = []
    needsReview = False

    # ---- 立地スコア ----
    if town in TOURIST_CORE:
        loc = 'core'; reasons.append(f'立地=観光地コア({town})')
    elif town in TOURIST_NEAR:
        loc = 'near'; reasons.append(f'立地=観光近接({town})')
    elif town in LOCAL_CORE:
        loc = 'local'; reasons.append(f'立地=地元コア({town})')
    else:
        loc = 'unknown'; reasons.append(f'立地=判定外/拠点({town or "不明"})')

    # ---- 業種クラス ----
    is_b2b = cat_has(cat, B2B_PREFIX) and not cat_has(cat, ('飲食', 'カフェ'))
    # 寺社/観光インフラは B2B プレフィックス(宗教法人 等)より優先
    # 宗教法人でも観光地コア立地なら参拝観光資源として扱う(例: 浅草寺)
    is_temple_tourism = cat_has(cat, INBOUND_INFRA_KEYS) or (cat_has(cat, ('宗教法人',)) and loc in ('core', 'near'))
    if is_temple_tourism:
        is_b2b = False
    # 観光地コアの商業施設(百貨店/SC/ディスカウント)は来街者向け小売として B2B から外す
    RETAIL_COMPLEX = ('多慶屋', '浅草ROX', 'アトレ', 'エキミセ', '吉池', 'ニトリ', 'ユニクロ', 'ジンズ', 'JINS')
    if loc in ('core', 'near') and any(rc in (st.get('name','') or '') for rc in RETAIL_COMPLEX):
        is_b2b = False
        reasons.append('観光地立地の来街者向け小売(B2B除外)')
    is_pet = cat_has(cat, PET_KEYS)
    is_group = cat_has(cat, GROUP_KEYS)
    is_infra = cat_has(cat, INBOUND_INFRA_KEYS) or is_temple_tourism
    is_food_visual = cat.startswith('飲食') or cat_has(cat, INBOUND_VISUAL_KEYS)
    is_retail_souvenir = cat_has(cat, INBOUND_RETAIL_KEYS)

    # ---- inboundReady の素点 (0-3) ----
    ir = 0
    if loc == 'core': ir += 2
    elif loc == 'near': ir += 1
    if is_infra: ir += 2; reasons.append('業種=観光インフラ/寺社/宿泊')
    if is_food_visual: ir += 1; reasons.append('業種=視覚/体験で楽しめる飲食')
    if is_retail_souvenir and not is_b2b: ir += 1; reasons.append('業種=土産/物販需要')
    if heFit.startswith('高'): ir += 1; reasons.append('hotelEastFit=高')
    if isinstance(rating, (int, float)) and rating >= 3.5: ir += 1; reasons.append(f'食べログ{rating}(観光集客力)')
    if site and 'en' in site.lower(): ir += 1; reasons.append('公式サイトに英語手掛かり')

    if is_b2b: ir = 0
    inboundReady = '高' if ir >= 4 else ('中' if ir >= 2 else '低')

    # ---- targetPrimary / targetMix ----
    mix = []
    if is_b2b:
        primary = '法人'
        mix = ['法人']
        reasons.append('業種=B2B/法人(来街接点薄)')
        inboundReady = '低'
    elif is_group:
        primary = 'インバウンド' if loc in ('core', 'near') else '地元'
        mix = [primary]
        reasons.append('山陽グループ内')
        needsReview = False
    elif is_pet:
        primary = '地元'
        mix = ['地元']
        reasons.append('業種=ペット(近隣常連が主)')
        inboundReady = '低'
    elif is_infra:
        primary = 'インバウンド'
        mix = ['インバウンド', '国内観光']
        reasons.append('業種=観光インフラ→訪日主')
    else:
        # 飲食・物販の本体ロジック
        if loc == 'core':
            if inboundReady == '高':
                primary = 'インバウンド'; mix = ['インバウンド', '国内観光']
            else:
                primary = '国内観光'; mix = ['国内観光', 'インバウンド']
        elif loc == 'near':
            primary = '国内観光'; mix = ['国内観光', '地元']
        elif loc == 'local':
            primary = '地元'; mix = ['地元', '国内観光'] if (is_food_visual and isinstance(rating,(int,float)) and rating>=3.5) else ['地元']
        else:
            primary = '要確認'; mix = []; needsReview = True
            reasons.append('立地・業種から確信不足→要確認')

    # 補助軸（ファミリー/シニア/富裕層/若者）
    aux = []
    if cat_has(cat, ('動物病院', 'ペット', '犬カフェ', '猫カフェ')): aux.append('ファミリー')
    if cat_has(cat, ('和菓子', '甘味', '料亭', 'うなぎ')): aux.append('シニア')
    if cat_has(cat, ('クラフトビール', 'バー', 'カフェバー', 'カラオケ', 'ゴルフバー')): aux.append('若者')
    if isinstance(rating, (int, float)) and rating >= 3.8 and cat.startswith('飲食'): aux.append('富裕層/グルメ')

    # ---- viewer 連動タグ ----
    tagmap = {'インバウンド': 'インバウンド向け', '国内観光': '国内観光', '地元': '地元', '法人': '法人'}
    tags = []
    for m in (mix or [primary]):
        t = tagmap.get(m)
        if t and t not in tags: tags.append(t)
    if inboundReady == '高' and 'インバウンド向け' not in tags:
        pass  # 高でも primary 地元なら付けない（mixに無ければ付与しない=誤フィルタ防止）
    for a in aux:
        if a not in tags: tags.append(a)

    return {
        'targetPrimary': primary,
        'targetMix': mix or [primary],
        'inboundReady': inboundReady,
        'auxSegments': aux,
        'tags': tags,
        'reason': ' / '.join(reasons),
        'needsReview': needsReview,
    }


patch = {}
for st in stores:
    patch[st['storeId']] = classify(st)

json.dump(patch, open(OUT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)

# ---- 集計サマリ（標準出力）----
from collections import Counter
cp = Counter(v['targetPrimary'] for v in patch.values())
ci = Counter(v['inboundReady'] for v in patch.values())
nr = sum(1 for v in patch.values() if v['needsReview'])
print('PRIMARY:', dict(cp))
print('INBOUND_READY:', dict(ci))
print('NEEDS_REVIEW:', nr)
# インバウンド手形セレクション候補
sel = [sid for sid, v in patch.items()
       if v['inboundReady'] == '高' and 'インバウンド向け' in v['tags']]
print('INBOUND_HIGH (selection pool):', len(sel))
