#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
高德 POI 补电话 v3（品牌词匹配版）。

为什么要改：单纯比字符串相似度会把「美华汽车服务」配到「路华汽车服务」、「金轮子」配到「振鹏」——
中文名只差一个字，相似度仍在 0.8 以上，但完全是两家店。v3 改成比「品牌词」：

  备案名 东莞市万江新盈科汽车维修站
    → 去城市前缀、去公司后缀、去镇名前缀、切掉通用词（汽车维修/服务/销售…）→ 品牌词「新盈科」
  门头名 盈科汽车服务部 → 品牌词「盈科」
  两者互相包含 → 同一家；「美华」vs「路华」→ 不包含，判为不同店，宁可不给电话。

用法:
    export AMAP_KEY=xxx
    export AMAP_CITY=东莞
    python3 enrich_phone.py 输入.csv [输出.csv] [--limit N]
支持断点续跑，限速约 3 次/秒。置信度 A=可直接用，B=需人工抽检。
"""
import csv, json, os, re, sys, time, unicodedata
import urllib.parse, urllib.request

KEY = os.environ.get("AMAP_KEY", "").strip()
CITY = os.environ.get("AMAP_CITY", "东莞")
API = "https://restapi.amap.com/v3/place/text"
RATE = 0.34
EXTRA = ["tel", "conf", "match", "poi_name", "poi_addr"]

CITY_PREFIX = ("东莞市", "深圳市", "惠州市", "赣州市", "广东省")
SUFFIX_RE = re.compile(r"(有限责任公司|有限公司|分公司|个体工商户|（个体工商户）|\(个体工商户\))$")
GENERIC = ["汽车维修服务", "汽车维修", "汽车修理", "汽车修配", "汽车服务", "汽车销售", "汽车贸易",
           "汽车美容", "汽车养护", "汽车配件", "汽车用品", "汽车", "汽修", "机动车",
           "维修服务", "维修", "修理", "修配", "服务", "销售", "贸易", "配件", "轮胎",
           "美容", "养护", "用品", "店", "厂", "中心", "站", "部", "行"]


def norm(s):
    s = unicodedata.normalize("NFKC", s or "")
    return re.sub(r"[\s\u3000·,，.。()（）\-—_]", "", s)


def core(name):
    s = norm(name)
    for p in CITY_PREFIX:
        if s.startswith(p):
            s = s[len(p):]
            break
    return SUFFIX_RE.sub("", s)


def brand(name, town=""):
    s = core(name)
    if town and s.startswith(town):
        s = s[len(town):]
    for g in GENERIC:
        i = s.find(g)
        if i >= 2:
            return s[:i]
    return s


def same_shop(a, b):
    if not a or not b:
        return False
    if a == b:
        return True
    return len(a) >= 2 and len(b) >= 2 and (a in b or b in a)


def search(kw):
    q = {"key": KEY, "keywords": kw, "city": CITY, "citylimit": "true",
         "offset": 10, "page": 1, "extensions": "base"}
    url = API + "?" + urllib.parse.urlencode(q)
    req = urllib.request.Request(url, headers={"User-Agent": "qx-lead/3.0"})
    with urllib.request.urlopen(req, timeout=12) as r:
        return json.loads(r.read().decode("utf-8")).get("pois") or []


def judge(row, pois):
    nm = row.get("企业名称") or row.get("单位名称") or ""
    town = norm(row.get("镇区") or row.get("地区") or row.get("区") or "")
    tb = brand(nm, town)
    best = None
    for p in pois:
        tel = (p.get("tel") or "").strip()
        if not tel:
            continue
        pname = p.get("name", "") or ""
        paddr = p.get("address", "") or ""
        if isinstance(paddr, list):
            paddr = " ".join(map(str, paddr))
        padname = p.get("adname", "") or ""      # 区/县名单独返回，深圳这类地址字段不含区名
        pb = brand(pname, town)
        if not same_shop(tb, pb):
            continue
        town_hit = bool(town) and (town in paddr or town in pname or town in padname)
        conf = "A" if town_hit else "B"
        score = 1.0 if town_hit and tb == pb else (0.8 if town_hit else 0.6)
        if best is None or score > best[0]:
            best = (score, tel, conf, round(score, 2), pname, paddr)
    if not best:
        return "", "", "", "", ""
    return best[1], best[2], best[3], best[4], best[5]


def main():
    if not KEY:
        sys.exit("缺少 AMAP_KEY")
    if len(sys.argv) < 2:
        sys.exit("用法: python3 enrich_phone.py 输入.csv [输出.csv] [--limit N]")
    src = sys.argv[1]
    dst = sys.argv[2] if len(sys.argv) > 2 and not sys.argv[2].startswith("--") else src.replace(".csv", "-tel.csv")
    limit = int(sys.argv[sys.argv.index("--limit") + 1]) if "--limit" in sys.argv else None

    with open(src, encoding="utf-8-sig") as f:
        rows = list(csv.DictReader(f))
    if limit:
        rows = rows[:limit]

    done = {}
    if os.path.exists(dst):
        with open(dst, encoding="utf-8-sig") as f:
            for r in csv.DictReader(f):
                done[norm(r.get("企业名称", ""))] = r

    fields = list(rows[0].keys()) + [x for x in EXTRA if x not in rows[0]]
    A = B = miss = err = 0
    with open(dst, "w", newline="", encoding="utf-8-sig") as f:
        w = csv.DictWriter(f, fieldnames=fields)
        w.writeheader()
        for i, row in enumerate(rows, 1):
            nm = row.get("企业名称") or row.get("单位名称") or ""
            if not nm:
                continue
            if norm(nm) in done:
                w.writerow(done[norm(nm)])
                continue
            rec = dict(row)
            try:
                pois = search(nm)
                c = core(nm)
                town = norm(row.get("镇区") or row.get("地区") or row.get("区") or "")
                if town and c.startswith(town) and len(c) > len(town) + 2:
                    seen = {p.get("id") for p in pois}
                    pois += [p for p in search(c[len(town):]) if p.get("id") not in seen]
                tel, conf, score, pname, paddr = judge(row, pois)
                rec.update({"tel": tel, "conf": conf, "match": score,
                            "poi_name": pname, "poi_addr": paddr})
                if conf == "A":
                    A += 1
                elif conf == "B":
                    B += 1
                else:
                    miss += 1
            except Exception as e:
                rec.update({"tel": "", "conf": "", "match": "", "poi_name": "ERR", "poi_addr": str(e)[:50]})
                err += 1
            w.writerow(rec)
            if i % 40 == 0:
                print(f"  {i}/{len(rows)}  A={A} B={B} 空={miss} 错={err}", flush=True)
            time.sleep(RATE)

    print(f"完成 {dst}：A 级 {A}（可直接用） B 级 {B}（需抽检） 空 {miss} 错 {err}")
    print("B 级务必人工确认再拨打，误配比空号代价高。")


if __name__ == "__main__":
    main()
