# -*- coding: utf-8 -*-
"""
言問散歩 ボードDB(board.json) ＋ 在庫・配布DB(inventory.json) ビルダ
出典: 在庫/設置数 正本スプレッドシート fileId 1rhKwpQVhNGhPeGdlklZjfF4rCKBM12ex
     ボード議事録 Doc 1G0ewHKh... / ボード進捗txt 1xtlGkAH...
ジオコード: 国土地理院 AddressSearch + meiten-master.json 既存座標流用
捏造禁止: 数値はスプレッドシート実値のみ。住所不明は null/不明。
"""
import json, os, re, time, urllib.parse, urllib.request

BASE = os.path.dirname(os.path.abspath(__file__))
GEO_CACHE = os.path.join(BASE, "_geocache.json")
GEO_INDEX = os.path.join(BASE, "_geo_index.json")

# 台東区中心域チェック（緩め: 台東区・隣接荒川/文京含む下町帯）
LAT_MIN, LAT_MAX = 35.69, 35.75
LNG_MIN, LNG_MAX = 139.74, 139.83

def load_cache():
    if os.path.exists(GEO_CACHE):
        return json.load(open(GEO_CACHE, encoding="utf-8"))
    return {}

def save_cache(c):
    json.dump(c, open(GEO_CACHE, "w", encoding="utf-8"), ensure_ascii=False, indent=1)

CACHE = load_cache()

def gsi_geocode(addr):
    """国土地理院ジオコーディング。台東中心域外/失敗は None。"""
    if not addr:
        return None
    # 〒・郵便番号を除去
    a = re.sub(r"〒?\s*\d{3}-?\d{4}", "", addr).strip()
    a = a.replace("　", " ")
    if a in CACHE:
        v = CACHE[a]
        return tuple(v) if v else None
    url = "https://msearch.gsi.go.jp/address-search/AddressSearch?q=" + urllib.parse.quote(a)
    try:
        with urllib.request.urlopen(url, timeout=25) as r:
            data = json.loads(r.read().decode("utf-8"))
    except Exception as e:
        print("  geocode error:", a, e)
        CACHE[a] = None
        return None
    time.sleep(0.4)
    if not data:
        CACHE[a] = None
        return None
    lng, lat = data[0]["geometry"]["coordinates"]
    CACHE[a] = [lat, lng]
    return (lat, lng)

# meiten既存座標インデックス（名寄せ用）
GEO = json.load(open(GEO_INDEX, encoding="utf-8"))
def norm_name(s):
    if not s: return ""
    s = s.lower()
    s = re.sub(r"[\s　　,，、。・･\-‐—–\(\)（）【】\[\]]", "", s)
    return s

NAME2COORD = {}
NAME2ID = {}
for g in GEO:
    for key in (g.get("name"), g.get("norm")):
        if key:
            nk = norm_name(key)
            if g.get("lat"):
                NAME2COORD.setdefault(nk, (g["lat"], g["lng"]))
            NAME2ID.setdefault(nk, g["id"])

def match_meiten(name):
    """店名 → (lat,lng,storeId) 既存マスター流用。部分一致も試す。"""
    nk = norm_name(name)
    if not nk:
        return (None, None, None)
    if nk in NAME2COORD or nk in NAME2ID:
        c = NAME2COORD.get(nk)
        return (c[0] if c else None, c[1] if c else None, NAME2ID.get(nk))
    # 部分一致（短い方が長い方に含まれる）
    for k in NAME2ID:
        if len(k) >= 3 and (k in nk or nk in k):
            c = NAME2COORD.get(k)
            return (c[0] if c else None, c[1] if c else None, NAME2ID.get(k))
    return (None, None, None)

def in_taito(lat, lng):
    return lat is not None and LAT_MIN <= lat <= LAT_MAX and LNG_MIN <= lng <= LNG_MAX

# ===================================================================
# 住所マスター（スプレッドシート ブロック④ Vol.14設置店住所・約50件）
# 出典: fileId 1rhKwpQVhNGhPeGdlklZjfF4rCKBM12ex「設置場所」セクション
# ===================================================================
ADDR = {
    "谷中ビアホール": "〒110-0002 東京都台東区上野桜木2-15-6",
    "喫茶 清浄無垢": "〒110-0002 東京都台東区上野桜木2-10-5",
    "CAFÉ＆BAR ROSSONERO": "〒110-0003 東京都台東区根岸1-5-13 南山ビル 2階",
    "チーズ料理専門店 チーズ谷": "〒110-0004 東京都台東区下谷2-3-1",
    "U BLUE": "〒110-0003 東京都台東区根岸1-3-20 2F",
    "ビストロ酒場5感": "〒110-0003 東京都台東区根岸1-3-20",
    "珈琲の店デン": "〒110-0003 東京都台東区根岸3-3-18",
    "酒処 侘助": "〒110-0013 東京都台東区入谷1-24-1",
    "とんかつ とん将": "〒111-0032 東京都台東区浅草3-22-4",
    "楽亀": "〒110-0013 東京都台東区入谷1-31-7",
    "四川料理 蜀香坊": "〒110-0004 東京都台東区下谷2-5-11",
    "オオイリヤ": "〒110-0004 東京都台東区下谷2-2-18",
    "龍一吟": "〒110-0003 東京都台東区根岸3-6-5",
    "花重谷中茶屋": "〒110-0001 東京都台東区谷中7-5-27",
    "はつね": "〒110-0003 東京都台東区根岸2-14-18",
    "サウナセンター鶯谷本店": "〒110-0004 東京都台東区下谷2-4-7",
    "浅草鳥きち": "〒111-0032 東京都台東区浅草2-27-8",
    "レオニダス浅草店 Cafe Rion": "〒111-0033 東京都台東区花川戸1-5-3",
    "炭火焼18": "〒110-0004 東京都台東区下谷1-6-9",
    "CAFE BEVANDARIA": "〒110-0003 東京都台東区根岸3-13-1",
    "カフェバーミヤミ": "〒111-0035 東京都台東区西浅草2-16-7",
    "Dining Bar 第三基地": "〒110-0004 東京都台東区下谷2-21-12",
    "カラオケMIX BAR 第四基地": "〒110-0003 東京都台東区根岸3-7-2 足立ビル 2階",
    "中国菜～道 dao～": "〒110-0001 東京都台東区谷中1-1",
    "江戸前煮干 中華そば きみはん 総本店": "〒110-0003 東京都台東区根岸3-3-18",
    "タラレバゴルフ＆バー": "〒110-0003 東京都台東区根岸1-1-21高野ビル 3F",
    "さけ処 川セ美": "〒110-0004 東京都台東区下谷2-24-11",
    "鮨 くり田": "〒110-0003 東京都台東区根岸1-1-26",
    "居酒屋 関所": "〒110-0003 東京都台東区根岸1-7-3",
    "合羽橋 栃木屋": "〒111-0035 東京都台東区西浅草3-7-1",
    "とんかつ とん平": "〒110-0003 東京都台東区根岸1-2-20",
    "Soy＆Co 1879": "〒111-0036 東京都台東区松が谷2-28-11",
    "ヤナカアパートメントストア Amazing Moments Yanaka": "〒110-0001 東京都台東区谷中7-525 ヤナカアパートメント102",
    "根津 松好": "〒113-0031 東京都文京区根津1-18-10",
    "縁": "〒116-0014 東京都荒川区東日暮里5-5-8",
    "炭火焼きと純米酒さとうともや": "〒116-0014 東京都荒川区東日暮里4-35-15",
    "徳岡官兵衛茶舗": "〒110-0003 東京都台東区根岸3-1-9",
    "ミカワシマバル スズナリ": "〒116-0002 東京都荒川区荒川3-63-3",
    "上野観光案内所": "〒110-8503 東京都台東区上野3-29-5 上野案内所2F",
    "東京観光情報センター 京成上野": "〒110-0007 東京都台東区上野公園1-60 京成上野駅改札口前",
    "madei": "〒111-0032 東京都台東区浅草7-3-12",
    "Mrs.Charlotte LA MAISON": "〒111-0032 東京都台東区浅草7-5-7",
    "feb's coffee ＆ scone": "〒111-0032 東京都台東区浅草3-1-1",
    "歌謡曲カフェLover’s": "〒110-0003 東京都台東区根岸1-1-16",
    "ダンスホール新世紀": "〒110-0003 東京都台東区根岸1-1-14 第二大島ビル",
    "清水観音堂": "〒110-0007 東京都台東区上野公園1-29",
    "寛永寺根本中堂": "〒110-0002 東京都台東区上野桜木1-14-11",
    "小野照崎神社": "〒110-0004 東京都台東区下谷2-13-14",
    "元三島神社": "〒110-0003 東京都台東区根岸1-7-11",
    "下町ミュージアム＋吉田屋": "〒110-0007 東京都台東区上野公園2-1",
    "アライ軒": "〒110-0004 東京都台東区下谷2-9-8",
    "よーかんちゃん": "〒110-0003 東京都台東区根岸1-2-12",
    # ブロック②に住所が併記された管理物件
    "店舗B（管理物件）": "〒110-0003 東京都台東区根岸３丁目1",
    "旧池田邸（管理物件）": "〒110-0003 東京都台東区根岸３丁目３−１７",
    "城北信金(管理物件)": "〒110-0003 東京都台東区根岸3-18-23",
    "美容室（管理物件）": "〒110-0003 東京都台東区下谷3-2-3",
    "旧種村邸（管理物件）": "〒110-0004 東京都台東区下谷1-11-14",
    "言問宿（管理物件）": "東京都台東区下谷1-12-11",
    "言問茶屋（管理物件）": "東京都台東区下谷１丁目１２−１１",
    "旧伊達邸 （管理物件）": "東京都台東区下谷1丁目5－35",
}

# 公開情報で住所が確認できる施設・寺社・著名店のみ補完（GR19: 確度=確認済の公的住所のみ。
# 個人名・配布手法・団体名など住所不定のものは補完しない=pendingのまま）。
ADDR_SUPPLEMENT = {
    "真源寺": "東京都台東区下谷1-12-16",            # 入谷鬼子母神
    "三島神社": "東京都台東区下谷3-7-5",
    "根津神社": "東京都文京区根津1-28-9",
    "浅草神社": "東京都台東区浅草2-3-1",
    "国立国会図書館（関西館含む）": "東京都千代田区永田町1-10-1",  # 東京本館
    "ROUTE BOOKS": "東京都台東区東上野4-14-3",
    "浅草見番": "東京都台東区浅草3-33-5",
    "根岸三平堂": "東京都台東区根岸2-10-12",
    "二木商会・二木の菓子": "東京都台東区上野6-16-3",  # アメ横 二木の菓子(本店)
}

def resolve_coords(name, addr):
    """1) meiten既存座標 2) GSIジオコード(住所/補完) の順。台東域チェック。"""
    lat, lng, sid = match_meiten(name)
    if lat is not None and in_taito(lat, lng):
        return lat, lng, sid, "meiten-master"
    # GSI
    a = addr or ADDR.get(name) or ADDR_SUPPLEMENT.get(name)
    if a:
        c = gsi_geocode(a)
        if c and in_taito(c[0], c[1]):
            return c[0], c[1], sid, "gsi"
        if c:  # 域外でも値はある（文京/荒川の隣接店）
            return c[0], c[1], sid, "gsi-outside"
    # meiten座標が域外でもあれば返す（最後の砦）
    if lat is not None:
        return lat, lng, sid, "meiten-outside"
    return None, None, sid, "pending"

# ===================================================================
# OUTPUT 1: board.json （インフォメーションボード = 言問ボード）
# 出典: スプレッドシート ブロック③ + 議事録
# ===================================================================
# 列: Vol.7,8,9,10,11,12,13,14,15  担当=加藤
# 値（空欄=null）。※0=近日設置予定
BOARD_ROWS = [
    # name, [v7,v8,v9,v10,v11,v12,v13,v14,v15], note
    ("はぎわら青果店", [15,15,None,0,30,0,0,35,None], ""),
    ("栃木屋",        [None,5,5,0,52,10,0,35,None], "合羽橋 栃木屋"),
    ("はつね",        [5,None,None,0,15,5,0,40,None], ""),
    ("関所",          [None,None,None,0,36,10,0,10,None], "居酒屋 関所"),
    ("関口さんの隣",  [7,None,None,0,20,0,0,0,None], ""),
    ("松崎さんの駐車場",[None,None,None,0,0,0,0,0,None], ""),
    ("大日印刷",      [None,5,None,15,20,0,0,10,None], "印刷所"),
    ("割烹 たむら",   [None,None,15,None,None,15,None,None,None], ""),
    ("荒砥牛乳店",    [10,None,15,None,15,None,None,None,None], ""),
    ("東上野 河井邸", [None,10,15,None,None,37,None,None,None], "河合邸前 候補(議事録)"),
    ("パークジャパン駐車場①", [12,15,None,None,None,None,None,None,None], "台東区根岸3丁目7-11"),
]
BOARD_ADDR = {
    "はぎわら青果店": "東京都台東区根岸3丁目",  # ブロック②「はぎわら青果」住所なし→丁目止まり
    "栃木屋": ADDR["合羽橋 栃木屋"],
    "はつね": ADDR["はつね"],
    "関所": ADDR["居酒屋 関所"],
    "大日印刷": None,
    "パークジャパン駐車場①": "東京都台東区根岸3-7-11",
}

VOLS_BOARD = ["vol7","vol8","vol9","vol10","vol11","vol12","vol13","vol14","vol15"]

def board_status(counts_list):
    """設置済/予定/停止 判定。直近(v14/v13)で正値=設置済、0のみ=予定、全null=停止/履歴。"""
    v14 = counts_list[7]
    v13 = counts_list[6]
    # 直近で正の冊数
    if (v14 and v14 > 0):
        return "設置済"
    if (v14 == 0):
        return "予定"  # ※0=近日中に設置予定（スプレッドシート注記）
    # v14がnull → 過去設置のみ。直近Volで配本が途絶
    if any(c for c in counts_list if c):
        return "停止"
    return "予定"

boards = []
print("=== BOARD geocoding ===")
for name, cl, note in BOARD_ROWS:
    addr = BOARD_ADDR.get(name) or ADDR.get(name)
    lat, lng, sid, src = resolve_coords(name, addr)
    counts = {}
    latest = None
    for i, v in enumerate(VOLS_BOARD):
        counts[v] = cl[i]
        if cl[i] is not None and cl[i] > 0:
            latest = cl[i]
    # latestCount = 直近で正値のVol（v15→v14→…）
    latest_count = None
    for v in reversed(VOLS_BOARD):
        if counts[v] is not None and counts[v] > 0:
            latest_count = counts[v]
            break
    st = board_status(cl)
    boards.append({
        "name": name,
        "tantou": "加藤",
        "address": addr if addr else "不明",
        "lat": lat, "lng": lng,
        "geocode": src,
        "storeId": sid,
        "status": st,
        "counts": counts,
        "latestCount": latest_count,
        "note": note,
        "contract": "月¥3,000/年¥36,000(原則・自社物件は無償含む)",
    })
    print(f"  {name}: {st} {src} lat={lat}")

board_meta = {
    "title": "言問ボード（インフォメーションボード）DB",
    "purpose": "言問散歩の設置告知ボード（軒下広告枠付き）の設置先・配本数・契約・状況のマスター。アプリ『ボード地図』タブが読む。",
    "sources": [
        "在庫/設置数 正本スプレッドシート fileId 1rhKwpQVhNGhPeGdlklZjfF4rCKBM12ex（ブロック③ インフォメーションボード）",
        "ボード設置計画・進捗 議事録 Doc 1G0ewHKh_YOPhfl4o7BIytC3u3CPMSl0Mmkl3mFxQhfE",
        "ボード進捗確認 txt 1xtlGkAHbtLnrMVFJeXzBKHB5IRM1OukQ",
        "ボード設計AI/PDF: 1FIW2xOiYFcM..(BF_sidenet_jyuki_boad) / 1IfxZwD-..(BF_jiritsu_board)",
    ],
    "geocoding": "国土地理院 AddressSearch + meiten-master.json 既存座標流用。台東中心域チェック(35.69-35.75/139.74-139.83)。取得不可=pending。",
    "tantou": "加藤さん中心（現地交渉・設置調整）。データ整備=宮内さん。",
    "contract": {
        "monthly": 3000, "yearly": 36000, "currency": "JPY",
        "note": "設置協力先への支払いは原則 月3,000円（自社物件は無償含む）。出典:議事録",
    },
    "targets": {"immediate": 30, "mid": 50, "final": 100,
                "note": "5〜6月で合計30箇所→中期50→最終100箇所。30到達後に軒下広告(ポスター枠)販売テスト開始。出典:議事録"},
    "businessModel": "ボード右側のポスター枠を『軒下広告』(求人・地域告知)として販売し収益化。媒体の『どこで見られるか』明示で媒体価値向上。",
    "fabricationNote": "GR19準拠。冊数はスプレッドシート実値のみ。空欄=null。住所不明は『不明』。現状『設置済約6カ所・交渉/目安約20カ所』(議事録)とDB行数は集計タイミングが異なる。",
    "vol15Note": "Vol.15列はスプレッドシート上 全行空欄（配本前）。",
    "updated": "2026-06-07",
}

board_out = {"_meta": board_meta, "boards": boards}
json.dump(board_out, open(os.path.join(BASE, "board.json"), "w", encoding="utf-8"),
          ensure_ascii=False, indent=2)
save_cache(CACHE)
print("board.json written:", len(boards), "boards")
